1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core;
15
16 import ch.qos.logback.core.spi.AppenderAttachable;
17 import ch.qos.logback.core.spi.AppenderAttachableImpl;
18 import ch.qos.logback.core.util.InterruptUtil;
19
20 import java.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.concurrent.ArrayBlockingQueue;
24 import java.util.concurrent.BlockingQueue;
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public class AsyncAppenderBase<E> extends UnsynchronizedAppenderBase<E> implements AppenderAttachable<E> {
47
48 AppenderAttachableImpl<E> aai = new AppenderAttachableImpl<E>();
49 BlockingQueue<E> blockingQueue;
50
51
52
53
54 public static final int DEFAULT_QUEUE_SIZE = 256;
55 int queueSize = DEFAULT_QUEUE_SIZE;
56
57 int appenderCount = 0;
58
59 static final int UNDEFINED = -1;
60 int discardingThreshold = UNDEFINED;
61 boolean neverBlock = false;
62
63 Worker worker = new Worker();
64
65
66
67
68
69
70 public static final int DEFAULT_MAX_FLUSH_TIME = 1000;
71 int maxFlushTime = DEFAULT_MAX_FLUSH_TIME;
72
73
74
75
76
77
78
79
80
81
82
83
84 protected boolean isDiscardable(E eventObject) {
85 return false;
86 }
87
88
89
90
91
92
93
94 protected void preprocess(E eventObject) {
95 }
96
97 @Override
98 public void start() {
99 if (isStarted())
100 return;
101 if (appenderCount == 0) {
102 addError("No attached appenders found.");
103 return;
104 }
105 if (queueSize < 1) {
106 addError("Invalid queue size [" + queueSize + "]");
107 return;
108 }
109 blockingQueue = new ArrayBlockingQueue<E>(queueSize);
110
111 if (discardingThreshold == UNDEFINED)
112 discardingThreshold = queueSize / 5;
113 addInfo("Setting discardingThreshold to " + discardingThreshold);
114 worker.setDaemon(true);
115 worker.setName("AsyncAppender-Worker-" + getName());
116
117
118 super.start();
119 worker.start();
120 }
121
122 @Override
123 public void stop() {
124 if (!isStarted())
125 return;
126
127
128
129
130
131 super.stop();
132
133
134
135 worker.interrupt();
136
137 InterruptUtil interruptUtil = new InterruptUtil(context);
138
139 try {
140 interruptUtil.maskInterruptFlag();
141
142 worker.join(maxFlushTime);
143
144
145 if (worker.isAlive()) {
146 addWarn("Max queue flush timeout (" + maxFlushTime + " ms) exceeded. Approximately "
147 + blockingQueue.size() + " queued events were possibly discarded.");
148 } else {
149 addInfo("Queue flush finished successfully within timeout.");
150 }
151
152 } catch (InterruptedException e) {
153 int remaining = blockingQueue.size();
154 addError("Failed to join worker thread. " + remaining + " queued events may be discarded.", e);
155 } finally {
156 interruptUtil.unmaskInterruptFlag();
157 }
158 }
159
160 @Override
161 protected void append(E eventObject) {
162 if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) {
163 return;
164 }
165 preprocess(eventObject);
166 put(eventObject);
167 }
168
169 private boolean isQueueBelowDiscardingThreshold() {
170 return (blockingQueue.remainingCapacity() < discardingThreshold);
171 }
172
173 private void put(E eventObject) {
174 if (neverBlock) {
175 blockingQueue.offer(eventObject);
176 } else {
177 putUninterruptibly(eventObject);
178 }
179 }
180
181 private void putUninterruptibly(E eventObject) {
182 boolean interrupted = false;
183 try {
184 while (true) {
185 try {
186 blockingQueue.put(eventObject);
187 break;
188 } catch (InterruptedException e) {
189 interrupted = true;
190 }
191 }
192 } finally {
193 if (interrupted) {
194 Thread.currentThread().interrupt();
195 }
196 }
197 }
198
199 public int getQueueSize() {
200 return queueSize;
201 }
202
203 public void setQueueSize(int queueSize) {
204 this.queueSize = queueSize;
205 }
206
207 public int getDiscardingThreshold() {
208 return discardingThreshold;
209 }
210
211 public void setDiscardingThreshold(int discardingThreshold) {
212 this.discardingThreshold = discardingThreshold;
213 }
214
215 public int getMaxFlushTime() {
216 return maxFlushTime;
217 }
218
219 public void setMaxFlushTime(int maxFlushTime) {
220 this.maxFlushTime = maxFlushTime;
221 }
222
223
224
225
226
227
228 public int getNumberOfElementsInQueue() {
229 return blockingQueue.size();
230 }
231
232 public void setNeverBlock(boolean neverBlock) {
233 this.neverBlock = neverBlock;
234 }
235
236 public boolean isNeverBlock() {
237 return neverBlock;
238 }
239
240
241
242
243
244
245
246
247
248
249 public int getRemainingCapacity() {
250 return blockingQueue.remainingCapacity();
251 }
252
253 public void addAppender(Appender<E> newAppender) {
254 if (appenderCount == 0) {
255 appenderCount++;
256 addInfo("Attaching appender named [" + newAppender.getName() + "] to AsyncAppender.");
257 aai.addAppender(newAppender);
258 } else {
259 addWarn("One and only one appender may be attached to AsyncAppender.");
260 addWarn("Ignoring additional appender named [" + newAppender.getName() + "]");
261 }
262 }
263
264 public Iterator<Appender<E>> iteratorForAppenders() {
265 return aai.iteratorForAppenders();
266 }
267
268 public Appender<E> getAppender(String name) {
269 return aai.getAppender(name);
270 }
271
272 public boolean isAttached(Appender<E> eAppender) {
273 return aai.isAttached(eAppender);
274 }
275
276 public void detachAndStopAllAppenders() {
277 aai.detachAndStopAllAppenders();
278 }
279
280 public boolean detachAppender(Appender<E> eAppender) {
281 return aai.detachAppender(eAppender);
282 }
283
284 public boolean detachAppender(String name) {
285 return aai.detachAppender(name);
286 }
287
288 class Worker extends Thread {
289
290 public void run() {
291 AsyncAppenderBase<E> parent = AsyncAppenderBase.this;
292 AppenderAttachableImpl<E> aai = parent.aai;
293
294
295 while (parent.isStarted()) {
296 try {
297 List<E> elements = new ArrayList<E>();
298 E e0 = parent.blockingQueue.take();
299 elements.add(e0);
300 parent.blockingQueue.drainTo(elements);
301 for (E e : elements) {
302 aai.appendLoopOnAppenders(e);
303 }
304 } catch (InterruptedException e1) {
305
306 break;
307 }
308 }
309
310 addInfo("Worker thread will flush remaining events before exiting. ");
311
312 for (E e : parent.blockingQueue) {
313 aai.appendLoopOnAppenders(e);
314 parent.blockingQueue.remove(e);
315 }
316
317 aai.detachAndStopAllAppenders();
318 }
319 }
320 }