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;
023import ch.qos.logback.core.util.ReentryGuard;
024import ch.qos.logback.core.util.ReentryGuardFactory;
025
026/**
027 * Similar to {@link AppenderBase} except that derived appenders need to handle thread
028 * synchronization on their own.
029 * 
030 * @author Ceki Gülcü
031 * @author Ralph Goers
032 */
033abstract public class UnsynchronizedAppenderBase<E> extends ContextAwareBase implements Appender<E> {
034
035    protected volatile boolean started = false;
036    /**
037     * The guard prevents an appender from repeatedly calling its own doAppend
038     * method.
039     *
040     * @since 1.5.21
041     */
042    private ReentryGuard  reentryGuard;
043
044    /**
045     * Appenders are named.
046     */
047    protected String name;
048
049    private FilterAttachableImpl<E> fai = new FilterAttachableImpl<E>();
050
051    public String getName() {
052        return name;
053    }
054
055    private int statusRepeatCount = 0;
056    private int exceptionCount = 0;
057
058    static final int ALLOWED_REPEATS = 3;
059
060    public void doAppend(E eventObject) {
061        if (!this.started) {
062            if (statusRepeatCount++ < ALLOWED_REPEATS) {
063                addStatus(new WarnStatus("Attempted to append to non started appender [" + name + "].", this));
064            }
065            return;
066        }
067
068        // prevent re-entry.
069        if (reentryGuard.isLocked()) {
070            return;
071        }
072
073        try {
074            reentryGuard.lock();
075
076            if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
077                return;
078            }
079
080            // ok, we now invoke derived class' implementation of append
081            this.append(eventObject);
082
083        } catch (Exception e) {
084            if (exceptionCount++ < ALLOWED_REPEATS) {
085                addError("Appender [" + name + "] failed to append.", e);
086            }
087        } finally {
088            reentryGuard.unlock();
089        }
090    }
091
092    abstract protected void append(E eventObject);
093
094    /**
095     * Set the name of this appender.
096     */
097    public void setName(String name) {
098        this.name = name;
099    }
100
101    public void start() {
102        this.reentryGuard = buildReentryGuard();
103        started = true;
104    }
105
106    /**
107     * Create a {@link ReentryGuard} instance used by this appender to prevent
108     * recursive/re-entrant calls to {@link #doAppend(Object)}.
109     *
110     * <p>The default implementation returns a no-op guard produced by
111     * {@link ReentryGuardFactory#makeGuard(ch.qos.logback.core.util.ReentryGuardFactory.GuardType)}
112     * using {@code GuardType.NOP}. Subclasses that require actual re-entry
113     * protection (for example using a thread-local or lock-based guard) should
114     * override this method to return an appropriate {@link ReentryGuard}
115     * implementation.</p>
116     *
117     * <p>Contract/expectations:
118     * <ul>
119     *   <li>Called from {@link #start()} to initialize the appender's guard.</li>
120     *   <li>Implementations should be lightweight and thread-safe.</li>
121     *   <li>Return value must not be {@code null}.</li>
122     * </ul>
123     * </p>
124     *
125     * @return a non-null {@link ReentryGuard} used to detect and prevent
126     *         re-entrant appends. By default this is a no-op guard.
127     * @since 1.5.21
128     */
129    protected ReentryGuard buildReentryGuard() {
130        return ReentryGuardFactory.makeGuard(ReentryGuardFactory.GuardType.NOP);
131    }
132
133    public void stop() {
134        started = false;
135    }
136
137    public boolean isStarted() {
138        return started;
139    }
140
141    public String toString() {
142        return this.getClass().getName() + "[" + name + "]";
143    }
144
145    public void addFilter(Filter<E> newFilter) {
146        fai.addFilter(newFilter);
147    }
148
149    public void clearAllFilters() {
150        fai.clearAllFilters();
151    }
152
153    public List<Filter<E>> getCopyOfAttachedFiltersList() {
154        return fai.getCopyOfAttachedFiltersList();
155    }
156
157    public FilterReply getFilterChainDecision(E event) {
158        return fai.getFilterChainDecision(event);
159    }
160}