Package edu.brown.logging

Source Code of edu.brown.logging.RingBufferAppender

package edu.brown.logging;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.log4j.Appender;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.LoggingEvent;

import edu.brown.utils.CollectionUtil;
import edu.brown.utils.StringUtil;

/**
* An appender that stores LoggingEvents in a ringbuffer (in-memory)
* and allows to retrieve the latest log messages.
* @author rschwarzkopf
* @author pavlo
* http://ds.informatik.uni-marburg.de/~fallenbeck/ICSd/javadoc/0.9.16/de/fb12/ics/logging/RingbufferAppender.html
*/
public class RingBufferAppender extends AppenderSkeleton {
    private static final Logger LOG = Logger.getLogger(RingBufferAppender.class);
   
    private static final int DEFAULT_SIZE = 1000;
   
    private LoggingEvent[] eventRing;
    private int currentPosition;
    private long counter;
    private int stackOffset = -1;
   
    private boolean useFastLocation = false;
    private boolean storeLocation = false;
    private boolean storeThreadName = false;
   
    /**
     * Create an appender instance.
     * @param bufferSize The size of the ringbuffer.
     */
    public RingBufferAppender() {
        this.init(DEFAULT_SIZE);
    }
   
    public RingBufferAppender(int size) {
        this.init(size);
    }
   
    private void init(int size) {
        this.eventRing = new LoggingEvent[size];
        this.currentPosition = -1;
        this.counter = 0;
        if (LOG.isDebugEnabled())
            LOG.debug(String.format("Initialized appender with new buffer [size=%d, useFastLocation=%s, storeLocation=%s, storeThreadName=%s, layout=%s]",
                                    size, this.useFastLocation, this.storeLocation, this.storeThreadName,
                                    (this.getLayout() != null ? this.getLayout().getClass().getSimpleName() : null)));
    }
   
    public void setUseFastLocation(boolean val) {
        this.useFastLocation = val;
    }
    public void setStoreLocation(boolean val) {
        this.storeLocation = val;
    }
    public void setStoreThreadName(boolean val) {
        this.storeThreadName = val;
    }
   
    public void setSize(int size) {
        this.init(size);
    }
    /**
     * Return the size of the ringbuffer.
     * @return Size of ringbuffer
     */
    public int getSize() {
        return this.eventRing.length;
    }
                
    @Override
    protected void append(LoggingEvent event) {
        if (this.useFastLocation) {
            if (this.stackOffset < 0) this.stackOffset = FastLoggingEvent.getStackOffset(event);
            event = new FastLoggingEvent(event, this.stackOffset);
        }
        if (this.storeLocation) event.getLocationInformation();
        if (this.storeThreadName) event.getThreadName();
       
        int position = -1;
        synchronized (this) {
            this.currentPosition = position = ++this.currentPosition % this.eventRing.length;
            this.counter++;
        } // SYNCH
        this.eventRing[position] = event;
//        assert(event.getLoggerName().contains("Handler") == false) : event;
    }

    @Override
    public void close() {
        // free memory
        for (int i = 0; i < this.eventRing.length; i++)
            this.eventRing[i] = null;
    }

    @Override
    public boolean requiresLayout() {
        return true;
    }
   
    /**
     * Returns the number of lines logged since ICS startup
     * @return  Number of lines logged
     */
    public long getLoggedLines() {
        return this.counter;
    }
   
    public LoggingEvent[] getLogEvents() {
        // create a snapshot by copying the complete storage
        LoggingEvent[] events = new LoggingEvent[this.eventRing.length];
        System.arraycopy(this.eventRing, 0, events, 0, this.eventRing.length);
       
        // store the index of the next element to be stored in the rrStorage
        // this should be the oldest entry
        int nextEntryIndex = (this.currentPosition + 1) % this.eventRing.length;
       
        LoggingEvent[] sortedEvents;
        if (events[nextEntryIndex] == null) {
            // event entry has not yet been filled completely, start at zero
            sortedEvents = new LoggingEvent[nextEntryIndex];
            for (int i = 0; i < sortedEvents.length; i++) {
                sortedEvents[i] = events[i];
            }
           
        // the storage position might have changed between copying the array and
        // calculating the next entry index, compare with oldest entry
        } else {
            // find the oldest entry by comparing the timestamps
            int oldestEntryIndex = -1;
            long oldestEntryTimestamp = Long.MAX_VALUE;
            LoggingEvent entry;
            long entryTimestamp;
            for (int entryIndex = 0; entryIndex < events.length; entryIndex++) {
                // FIXME <= ? start at the nextEntryIndex? Maybe backwards?
                if ((entry = events[entryIndex]) != null && (entryTimestamp = entry.getTimeStamp()) < oldestEntryTimestamp) {
                    oldestEntryIndex = entryIndex;
                    oldestEntryTimestamp = entryTimestamp;
                }
            } // FOR
           
            // if the next entry index and oldest entry index are different and have the same timestamp
            // (which only happens if much logging is done), use the next entry index
            if (nextEntryIndex != oldestEntryIndex && oldestEntryTimestamp == events[nextEntryIndex].getTimeStamp())
                oldestEntryIndex = nextEntryIndex;
           
            // sort the events into a new array
            //  LoggingEvent[] sortedEvents = new LoggingEvent[events.length];
            sortedEvents = new LoggingEvent[events.length];
            for (int i = 0; i < sortedEvents.length; i++) {
                sortedEvents[i] = events[oldestEntryIndex++ % events.length];
            }
        }
        // throw away the old array
        events = null;
       
        return (sortedEvents);
    }

    public String[] getLogMessages() {
        LoggingEvent[] events = this.getLogEvents();
        String[] ret = new String[events.length];
        Layout layout = this.getLayout();
        for (int i = 0; i < events.length; i++) {
            ret[i] = layout.format(events[i]);
        } // FOR
        return (ret);
    }
   
   
    @SuppressWarnings("unchecked")
    public static void enableRingBufferAppender(Logger logger, int bufferSize) {
        Layout l = null;
        if (LOG.isDebugEnabled())
            LOG.debug(logger + " => " + logger.getAllAppenders());
        for (Object o : CollectionUtil.iterable(logger.getAllAppenders())) {
            Appender a = (Appender)o;
            l = a.getLayout();
        } // FOR
        if (l != null) {
            logger.removeAllAppenders();
            logger.addAppender(new RingBufferAppender(bufferSize));
            Logger.getRootLogger().info("Enabled RingBuffer logging for '" + logger.getName() + "'");
        }       
    }
   
    @SuppressWarnings("unchecked")
    public static RingBufferAppender getRingBufferAppender(Logger logger) {
        RingBufferAppender rba = null;
        if (LOG.isTraceEnabled())
            LOG.trace("Checking whether " + logger.getName() + " has a RingBufferAppender attached: " + CollectionUtil.list(logger.getAllAppenders()));
        for (Object o : CollectionUtil.iterable(logger.getAllAppenders())) {
            if (o instanceof RingBufferAppender) {
                rba = (RingBufferAppender)o;
                if (LOG.isDebugEnabled())
                    LOG.debug("Found " + rba + " for " + logger.getName());
                break;
            }
        } // FOR
        return (rba);
    }
   
    @SuppressWarnings("unchecked")
    public static Collection<LoggingEvent> getLoggingEvents(LoggerRepository repo) {
        Set<Logger> loggers = new HashSet<Logger>();
        for (Object o : CollectionUtil.iterable(repo.getCurrentLoggers())) {
            Logger logger = (Logger)o;
            RingBufferAppender rba = getRingBufferAppender(logger);
            if (rba != null) {
                if (LOG.isDebugEnabled())
                    LOG.debug(logger.getName() + " => " + rba + " / " + rba.getLayout());
                loggers.add(logger);
            }
        } // FOR
        if (loggers.isEmpty()) return (Collections.emptyList());
        return (getLoggingEvents(loggers.toArray(new Logger[0])));
    }
   
    @SuppressWarnings("unchecked")
    public static Collection<String> getLoggingMessages(LoggerRepository repo) {
        Set<RingBufferAppender> appenders = new HashSet<RingBufferAppender>();
        for (Object o : CollectionUtil.iterable(repo.getCurrentLoggers())) {
            Logger logger = (Logger)o;
            RingBufferAppender rba = getRingBufferAppender(logger);
            if (rba != null) {
                 if (LOG.isDebugEnabled())
                    LOG.debug(logger.getName() + " => " + rba + " / " + rba.getLayout());
                appenders.add(rba);
            }
        } // FOR
        if (appenders.isEmpty()) return (Collections.emptyList());
        return (getLoggingMessages(appenders.toArray(new RingBufferAppender[0])));
    }
   
    public static Collection<LoggingEvent> getLoggingEvents(Logger...loggers) {
        SortedSet<LoggingEvent> events = new TreeSet<LoggingEvent>(new Comparator<LoggingEvent>() {
            @Override
            public int compare(LoggingEvent o1, LoggingEvent o2) {
                return (int)(o1.timeStamp - o2.timeStamp);
            }
        });
        for (Logger log : loggers) {
            RingBufferAppender rba = getRingBufferAppender(log);
            if (rba != null) {
                CollectionUtil.addAll(events, rba.getLogEvents());
            }
        } // FOR
        return (events);
    }
   
    public static Collection<String> getLoggingMessages(RingBufferAppender...appenders) {
        List<LoggingEvent> events = new ArrayList<LoggingEvent>();
        Layout layout = null;
        for (RingBufferAppender rba : appenders) {
            LoggingEvent e[] = rba.getLogEvents();
            if (LOG.isDebugEnabled())
                LOG.debug("Got " + e.length + " LoggingEvents for " + rba);
            CollectionUtil.addAll(events, e);
            if (layout == null) layout = rba.getLayout();
        } // FOR
        if (events.isEmpty() == false) assert(layout != null);
       
        Collections.sort(events, new Comparator<LoggingEvent>() {
            @Override
            public int compare(LoggingEvent o1, LoggingEvent o2) {
                return (int)(o1.timeStamp - o2.timeStamp);
            }
        });
        List<String> messages = new ArrayList<String>();
        for (LoggingEvent event : events) {
            messages.add(layout.format(event));
        } // FOR
        return (messages);
    }
   
    public void dump(PrintStream out) {
        int width = 100;
        out.println(StringUtil.header(this.getClass().getSimpleName(), "=", width));
        for (String log : this.getLogMessages()) {
            out.println(log.trim());
        }
        out.println(StringUtil.repeat("=", width));
        out.flush();
    }
}
TOP

Related Classes of edu.brown.logging.RingBufferAppender

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.