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}