AsyncLogTarget.java 4.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the LICENSE file.
 */
package org.jivesoftware.util.log.output;

import org.jivesoftware.util.log.ErrorAware;
import org.jivesoftware.util.log.ErrorHandler;
import org.jivesoftware.util.log.LogEvent;
import org.jivesoftware.util.log.LogTarget;
import java.util.LinkedList;

/**
 * An asynchronous LogTarget that sends entries on in another thread.
 * It is the responsibility of the user of this class to start
 * the thread etc.
 * <p/>
 * <pre>
 * LogTarget mySlowTarget = ...;
 * AsyncLogTarget asyncTarget = new AsyncLogTarget( mySlowTarget );
 * Thread thread = new Thread( asyncTarget );
 * thread.setPriority( Thread.MIN_PRIORITY );
 * thread.start();
 * <p/>
 * logger.setLogTargets( new LogTarget[] { asyncTarget } );
 * </pre>
 *
 * @author <a href="mailto:peter@apache.org">Peter Donald</a>
 */
public class AsyncLogTarget extends AbstractTarget implements Runnable {

    private final LinkedList m_list;
    private final int m_queueSize;
    private final LogTarget m_logTarget;

    public AsyncLogTarget(final LogTarget logTarget) {
        this(logTarget, 15);
    }

    public AsyncLogTarget(final LogTarget logTarget, final int queueSize) {
        m_logTarget = logTarget;
        m_list = new LinkedList();
        m_queueSize = queueSize;
        open();
    }

    /**
     * Provide component with ErrorHandler.
     *
     * @param errorHandler the errorHandler
     */
    public synchronized void setErrorHandler(final ErrorHandler errorHandler) {
        super.setErrorHandler(errorHandler);

        if (m_logTarget instanceof ErrorAware) {
            ((ErrorAware)m_logTarget).setErrorHandler(errorHandler);
        }
    }

    /**
     * Process a log event by adding it to queue.
     *
     * @param event the log event
     */
    public void doProcessEvent(final LogEvent event) {
        synchronized (m_list) {
            final int size = m_list.size();
            while (m_queueSize <= size) {
                try {
                    m_list.wait();
                }
                catch (final InterruptedException ie) {
                    //This really should not occur ...
                    //Maybe we should log it though for
                    //now lets ignore it
                }
            }

            m_list.addFirst(event);

            if (size == 0) {
                //tell the "server" thread to wake up
                //if it is waiting for a queue to contain some items
                m_list.notify();
            }
        }
    }

    public void run() {
        //set this variable when thread is interupted
        //so we know we can shutdown thread soon.
        boolean interupted = false;

        while (true) {
            LogEvent event = null;

            synchronized (m_list) {
                while (null == event) {
                    final int size = m_list.size();

                    if (size > 0) {
                        event = (LogEvent)m_list.removeLast();

                        if (size == m_queueSize) {
                            //tell the "client" thread to wake up
                            //if it is waiting for a queue position to open up
                            m_list.notify();
                        }

                    }
                    else if (interupted || Thread.interrupted()) {
                        //ie there is nothing in queue and thread is interrupted
                        //thus we stop thread
                        return;
                    }
                    else {
                        try {
                            m_list.wait();
                        }
                        catch (final InterruptedException ie) {
                            //Ignore this and let it be dealt in next loop
                            //Need to set variable as the exception throw cleared status
                            interupted = true;
                        }
                    }
                }
            }


            try {
                //actually process an event
                m_logTarget.processEvent(event);
            }
            catch (final Throwable throwable) {
                getErrorHandler().error("Unknown error writing event.", throwable, event);
            }
        }
    }
}