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}