001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core;
015
016import java.util.List;
017
018import ch.qos.logback.core.filter.Filter;
019import ch.qos.logback.core.spi.ContextAwareBase;
020import ch.qos.logback.core.spi.FilterAttachableImpl;
021import ch.qos.logback.core.spi.FilterReply;
022import ch.qos.logback.core.status.WarnStatus;
023
024/**
025 * Similar to {@link AppenderBase} except that derived appenders need to handle thread
026 * synchronization on their own.
027 * 
028 * @author Ceki Gülcü
029 * @author Ralph Goers
030 */
031abstract public class UnsynchronizedAppenderBase<E> extends ContextAwareBase implements Appender<E> {
032
033    protected volatile boolean started = false;
034
035    // using a ThreadLocal instead of a boolean add 75 nanoseconds per
036    // doAppend invocation. This is tolerable as doAppend takes at least a few
037    // microseconds
038    // on a real appender
039    /**
040     * The guard prevents an appender from repeatedly calling its own doAppend
041     * method.
042     */
043    private ThreadLocal<Boolean> guard = new ThreadLocal<Boolean>();
044
045    /**
046     * Appenders are named.
047     */
048    protected String name;
049
050    private FilterAttachableImpl<E> fai = new FilterAttachableImpl<E>();
051
052    public String getName() {
053        return name;
054    }
055
056    private int statusRepeatCount = 0;
057    private int exceptionCount = 0;
058
059    static final int ALLOWED_REPEATS = 3;
060
061    public void doAppend(E eventObject) {
062        // WARNING: The guard check MUST be the first statement in the
063        // doAppend() method.
064
065        // prevent re-entry.
066        if (Boolean.TRUE.equals(guard.get())) {
067            return;
068        }
069
070        try {
071            guard.set(Boolean.TRUE);
072
073            if (!this.started) {
074                if (statusRepeatCount++ < ALLOWED_REPEATS) {
075                    addStatus(new WarnStatus("Attempted to append to non started appender [" + name + "].", this));
076                }
077                return;
078            }
079
080            if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
081                return;
082            }
083
084            // ok, we now invoke derived class' implementation of append
085            this.append(eventObject);
086
087        } catch (Exception e) {
088            if (exceptionCount++ < ALLOWED_REPEATS) {
089                addError("Appender [" + name + "] failed to append.", e);
090            }
091        } finally {
092            guard.set(Boolean.FALSE);
093        }
094    }
095
096    abstract protected void append(E eventObject);
097
098    /**
099     * Set the name of this appender.
100     */
101    public void setName(String name) {
102        this.name = name;
103    }
104
105    public void start() {
106        started = true;
107    }
108
109    public void stop() {
110        started = false;
111    }
112
113    public boolean isStarted() {
114        return started;
115    }
116
117    public String toString() {
118        return this.getClass().getName() + "[" + name + "]";
119    }
120
121    public void addFilter(Filter<E> newFilter) {
122        fai.addFilter(newFilter);
123    }
124
125    public void clearAllFilters() {
126        fai.clearAllFilters();
127    }
128
129    public List<Filter<E>> getCopyOfAttachedFiltersList() {
130        return fai.getCopyOfAttachedFiltersList();
131    }
132
133    public FilterReply getFilterChainDecision(E event) {
134        return fai.getFilterChainDecision(event);
135    }
136}