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