Package org.perf4j.logback

Source Code of org.perf4j.logback.AsyncCoalescingStatisticsAppender

/* Copyright (c) 2008-2009 HomeAway, Inc.
* All rights reserved.  http://www.perf4j.org
*
* Licensed 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 org.perf4j.logback;

import java.io.Flushable;
import java.util.Iterator;
import org.perf4j.GroupedTimingStatistics;
import org.perf4j.StopWatch;
import org.perf4j.helpers.GenericAsyncCoalescingStatisticsAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.AppenderBase;
import ch.qos.logback.core.spi.AppenderAttachable;
import ch.qos.logback.core.spi.AppenderAttachableImpl;

/**
* This logback Appender groups StopWatch log messages together to form GroupedTimingStatistics. At a scheduled interval
* the StopWatch log messages that currently exist in the buffer are pulled to create a single
* GroupedTimingStatistics instance that is then sent to any attached appenders.
* <p/>
* Note that any LoggingEvents which do NOT contain StopWatch objects are discarded. Also, this appender stores logged
* messages in a bounded buffer before sending those messages to downstream appenders. If the buffer becomes full then
* subsequent logs will be discarded until the buffer has time to clear. You can access the number of discarded
* messages using the getNumDiscardedMessages() method.
*
* @author Alex Devine
* @author Xu Huisheng
*/
public class AsyncCoalescingStatisticsAppender extends AppenderBase<LoggingEvent> implements AppenderAttachable<LoggingEvent> {
    // --- configuration options ---
    // note most configuration options are provided by the GenericAsyncCoalescingStatisticsAppender
    /**
     * DownstreamLogLevel option, converted to a Level object
     */
    private Level downstreamLogLevel = Level.INFO;

    // --- contained objects ---
    /**
     * This instance provides the main logic for this appender. This wrapper class just provides the logback-specific
     * parts.
     */
    private final GenericAsyncCoalescingStatisticsAppender baseImplementation =
            newGenericAsyncCoalescingStatisticsAppender();

    /**
     * The downstream appenders are contained in this AppenderAttachableImpl
     */
    private final AppenderAttachableImpl<LoggingEvent> downstreamAppenders = new AppenderAttachableImpl<LoggingEvent>();

    // --- options ---
    /**
     * The <b>TimeSlice</b> option represents the length of time, in milliseconds, of the window in which appended
     * LoggingEvents are coalesced to a single GroupedTimingStatistics and sent to downstream appenders.
     * Defaults to 30,000 milliseconds.
     *
     * @return the TimeSlice option.
     */
    public long getTimeSlice() {
        return baseImplementation.getTimeSlice();
    }

    /**
     * Sets the value of the <b>TimeSlice</b> option.
     *
     * @param timeSlice The new TimeSlice option, in milliseconds.
     */
    public void setTimeSlice(long timeSlice) {
        baseImplementation.setTimeSlice(timeSlice);
    }

    /**
     * The <b>ShutdownWaitMillis</b> option represents the length of time, in milliseconds,
     * that the appender should wait after the logging system shutdown commences, before forcibly
     * clearing asynchronous logging queues and interrupting the background queue-processing thread.
     *
     * If not set explicitly, the default shutdown timeout is 10 seconds.
     *
     * @return the ShutdownWaitMillis option.
     */
    public long getShutdownWaitMillis() {
        return baseImplementation.getShutdownWaitMillis();
    }

    /**
     * Sets the value of the <b>ShutdownWaitMillis</b> option.
     *
     * @param shutdownWaitMillis The new ShutdownWaitMillis option, in milliseconds.
     */
    public void setShutdownWaitMillis(long shutdownWaitMillis) {
        baseImplementation.setShutdownWaitMillis(shutdownWaitMillis);
    }

    /**
     * The <b>DownstreamLogLevel</b> option gets the Level of the GroupedTimingStatistics LoggingEvent that is sent to
     * downstream appenders. Since each GroupedTimingStatistics represents a view of a collection of single StopWatch
     * timing event, each of which may have been logged at different levels, this appender needs to decide on a single
     * Level to use to notify downstream appenders. Defaults to "INFO".
     *
     * @return The DownstreamLogLevel option as a String
     */
    public String getDownstreamLogLevel() {
        return downstreamLogLevel.toString();
    }

    /**
     * Sets the value of the <b>DownstreamLogLevel</b> option. This String must be one of the defined Level constants.
     *
     * @param downstreamLogLevel The new DownstreamLogLevel option.
     */
    public void setDownstreamLogLevel(String downstreamLogLevel) {
        this.downstreamLogLevel = Level.toLevel(downstreamLogLevel);
    }

    /**
     * The <b>CreateRollupStatistics</b> option is used to determine whether "rollup" statistics should be created.
     * If the tag name of a StopWatch in a log message contains periods, then the GroupedTimingStatistics will be
     * created as if each substring of the tag up to the period was also logged with a separate StopWatch instance.
     * For example, suppose a StopWatch was logged with a tag of "requests.specificReq.PASS". For grouping purposes
     * a StopWatch entry would be logged under each of the following tags:
     * <ul>
     * <li>requests
     * <li>requests.specificReq
     * <li>requests.specificReq.PASS
     * </ul>
     * This allows you to view statistics at both an individual and aggregated level. If there were other StopWatch
     * entries with a tag of "requests.specificReq.FAIL", then the data collected at the "requests.specificReq" level
     * would include BOTH PASS and FAIL events.
     *
     * @return The CreateRollupStatistics option.
     */
    public boolean isCreateRollupStatistics() {
        return baseImplementation.isCreateRollupStatistics();
    }

    /**
     * Sets the value of the <b>CreateRollupStatistics</b> option.
     *
     * @param createRollupStatistics The new CreateRollupStatistics option.
     */
    public void setCreateRollupStatistics(boolean createRollupStatistics) {
        baseImplementation.setCreateRollupStatistics(createRollupStatistics);
    }

    /**
     * The <b>QueueSize</b> option is used to control the size of the internal queue used by this appender to store
     * logged messages before they are sent to downstream appenders. Defaults to 1024. If set too small and the queue
     * fills up, then logged StopWatches will be discarded. The number of discarded messages can be accessed using the
     * {@link #getNumDiscardedMessages()} method.
     *
     * @return The QueueSize option.
     */
    public int getQueueSize() {
        return baseImplementation.getQueueSize();
    }

    /**
     * Sets the value of the <b>QueueSize</b> option.
     *
     * @param queueSize The new QueueSize option.
     */
    public void setQueueSize(int queueSize) {
        baseImplementation.setQueueSize(queueSize);
    }

    /**
     * The <b>StopWatchParserClassName</b> option is used to determine the class used to parse stop watch messages
     * into StopWatch instances. This defaults to the standard "org.perf4j.helpers.StopWatchParser" class.
     *
     * @return The StopWatchParserClassName option.
     */
    public String getStopWatchParserClassName() {
        return baseImplementation.getStopWatchParserClassName();
    }

    /**
     * Sets the value of the <b>StopWatchParserClassName</b> option.
     *
     * @param stopWatchParserClassName The new StopWatchParserClassName option.
     */
    public void setStopWatchParserClassName(String stopWatchParserClassName) {
        baseImplementation.setStopWatchParserClassName(stopWatchParserClassName);
    }

    public void setName(String name) {
        super.setName(name);
        baseImplementation.setName(name);
    }

    public void start() {
        super.start();
        //Start the underlying generic appender with a handler object that pumps statistics to the downstream appenders
        baseImplementation.start(new GenericAsyncCoalescingStatisticsAppender.GroupedTimingStatisticsHandler() {
            public void handle(GroupedTimingStatistics statistics) {
                LoggingEvent coalescedLoggingEvent =
                        new LoggingEvent(Logger.class.getName(),
                                         getLoggerContext().getLogger(StopWatch.DEFAULT_LOGGER_NAME),
                                         downstreamLogLevel,
                                         "{}",
                                         null,
                                         new Object[] {statistics});
                try {
                    synchronized(downstreamAppenders) {
                        downstreamAppenders.appendLoopOnAppenders(coalescedLoggingEvent);
                    }
                } catch (Exception e) {
                    addError("Exception calling append with GroupedTimingStatistics on downstream appender",
                            e);
                }
            }

            public void error(String errorMessage) {
                addError(errorMessage);
            }
        });

    }

    // --- attributes ---
    /**
     * Returns the number of StopWatch messages that have been discarded due to the queue being full.
     *
     * @return The number of discarded messages.
     */
    public int getNumDiscardedMessages() {
        return baseImplementation.getNumDiscardedMessages();
    }

    // --- appender attachable methods ---
    public void addAppender(Appender<LoggingEvent> newAppender) {
        synchronized(downstreamAppenders) {
            downstreamAppenders.addAppender(newAppender);
        }
    }

    public Iterator<Appender<LoggingEvent>> iteratorForAppenders() {
        synchronized(downstreamAppenders) {
            return downstreamAppenders.iteratorForAppenders();
        }
    }

    public Appender<LoggingEvent> getAppender(String name) {
        synchronized(downstreamAppenders) {
            return downstreamAppenders.getAppender(name);
        }
    }

    public boolean isAttached(Appender<LoggingEvent> appender) {
        synchronized(downstreamAppenders) {
            return downstreamAppenders.isAttached(appender);
        }
    }

    public void detachAndStopAllAppenders() {
        synchronized(downstreamAppenders) {
            downstreamAppenders.detachAndStopAllAppenders();
        }
    }

    public boolean detachAppender(Appender<LoggingEvent> appender) {
        synchronized(downstreamAppenders) {
            return downstreamAppenders.detachAppender(appender);
        }
    }

    public boolean detachAppender(String name) {
        synchronized(downstreamAppenders) {
            return downstreamAppenders.detachAppender(name);
        }
    }

    // --- appender methods ---
    protected void append(LoggingEvent event) {
        baseImplementation.append(event.getFormattedMessage());
    }

    public void stop() {
        baseImplementation.stop();

        //close the downstream appenders
        synchronized (downstreamAppenders) {
            //first FLUSH any flushable downstream appenders (fix for PERFFORJ-22). Note we CAN NOT just flush and
            //close in one loop because this breaks in the case of a "diamond" relationship between appenders, where,
            //say, this appender has 2 attached GraphingStatisticsAppenders that each write to a SINGLE attached
            //FileAppender.
            for (Iterator<Appender<LoggingEvent>> iter = downstreamAppenders.iteratorForAppenders();
                 iter != null && iter.hasNext();) {
                Appender<LoggingEvent> appender = iter.next();
                if (appender instanceof Flushable) {
                    try {
                        ((Flushable)appender).flush();
                    } catch (Exception e) { /* Just eat the exception, we're closing down */ }
                }
            }

            //THEN close them
            for (Iterator<Appender<LoggingEvent>> iter = downstreamAppenders.iteratorForAppenders();
                iter != null && iter.hasNext();) {
                iter.next().stop();
            }
        }
        super.stop();
    }

    // --- helper methods ---
    /**
     * Creates the new GenericAsyncCoalescingStatisticsAppender that this instance will wrap.
     *
     * @return The newly created GenericAsyncCoalescingStatisticsAppender.
     */
    protected GenericAsyncCoalescingStatisticsAppender newGenericAsyncCoalescingStatisticsAppender() {
        return new GenericAsyncCoalescingStatisticsAppender();
    }

    /**
     * Returns the LoggerContext that logback will set for this appender
     *
     * @return The LoggerContext
     */
    LoggerContext getLoggerContext() {
        return (LoggerContext)getContext();
    }


}
TOP

Related Classes of org.perf4j.logback.AsyncCoalescingStatisticsAppender

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.