1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.status;
18
19 import java.io.Closeable;
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Queue;
25 import java.util.concurrent.ConcurrentLinkedQueue;
26 import java.util.concurrent.CopyOnWriteArrayList;
27 import java.util.concurrent.locks.Lock;
28 import java.util.concurrent.locks.ReadWriteLock;
29 import java.util.concurrent.locks.ReentrantLock;
30 import java.util.concurrent.locks.ReentrantReadWriteLock;
31
32 import org.apache.logging.log4j.Level;
33 import org.apache.logging.log4j.Marker;
34 import org.apache.logging.log4j.message.Message;
35 import org.apache.logging.log4j.message.MessageFactory;
36 import org.apache.logging.log4j.message.ParameterizedNoReferenceMessageFactory;
37 import org.apache.logging.log4j.simple.SimpleLogger;
38 import org.apache.logging.log4j.simple.SimpleLoggerContext;
39 import org.apache.logging.log4j.spi.AbstractLogger;
40 import org.apache.logging.log4j.util.PropertiesUtil;
41 import org.apache.logging.log4j.util.Strings;
42
43
44
45
46
47
48
49
50
51
52 public final class StatusLogger extends AbstractLogger {
53
54
55
56
57
58 public static final String MAX_STATUS_ENTRIES = "log4j2.status.entries";
59
60
61
62
63
64 public static final String DEFAULT_STATUS_LISTENER_LEVEL = "log4j2.StatusLogger.level";
65
66 private static final long serialVersionUID = 2L;
67
68 private static final String NOT_AVAIL = "?";
69
70 private static final PropertiesUtil PROPS = new PropertiesUtil("log4j2.StatusLogger.properties");
71
72 private static final int MAX_ENTRIES = PROPS.getIntegerProperty(MAX_STATUS_ENTRIES, 200);
73
74 private static final String DEFAULT_STATUS_LEVEL = PROPS.getStringProperty(DEFAULT_STATUS_LISTENER_LEVEL);
75
76
77 private static final StatusLogger STATUS_LOGGER = new StatusLogger(StatusLogger.class.getName(),
78 ParameterizedNoReferenceMessageFactory.INSTANCE);
79
80 private final SimpleLogger logger;
81
82 private final Collection<StatusListener> listeners = new CopyOnWriteArrayList<>();
83
84 @SuppressWarnings("NonSerializableFieldInSerializableClass")
85
86 private final ReadWriteLock listenersLock = new ReentrantReadWriteLock();
87
88 private final Queue<StatusData> messages = new BoundedQueue<>(MAX_ENTRIES);
89
90 @SuppressWarnings("NonSerializableFieldInSerializableClass")
91
92 private final Lock msgLock = new ReentrantLock();
93
94 private int listenersLevel;
95
96 private StatusLogger(final String name, final MessageFactory messageFactory) {
97 super(name, messageFactory);
98 this.logger = new SimpleLogger("StatusLogger", Level.ERROR, false, true, false, false, Strings.EMPTY,
99 messageFactory, PROPS, System.err);
100 this.listenersLevel = Level.toLevel(DEFAULT_STATUS_LEVEL, Level.WARN).intLevel();
101 }
102
103
104
105
106
107
108 public static StatusLogger getLogger() {
109 return STATUS_LOGGER;
110 }
111
112 public void setLevel(final Level level) {
113 logger.setLevel(level);
114 }
115
116
117
118
119
120
121 public void registerListener(final StatusListener listener) {
122 listenersLock.writeLock().lock();
123 try {
124 listeners.add(listener);
125 final Level lvl = listener.getStatusLevel();
126 if (listenersLevel < lvl.intLevel()) {
127 listenersLevel = lvl.intLevel();
128 }
129 } finally {
130 listenersLock.writeLock().unlock();
131 }
132 }
133
134
135
136
137
138
139 public void removeListener(final StatusListener listener) {
140 closeSilently(listener);
141 listenersLock.writeLock().lock();
142 try {
143 listeners.remove(listener);
144 int lowest = Level.toLevel(DEFAULT_STATUS_LEVEL, Level.WARN).intLevel();
145 for (final StatusListener statusListener : listeners) {
146 final int level = statusListener.getStatusLevel().intLevel();
147 if (lowest < level) {
148 lowest = level;
149 }
150 }
151 listenersLevel = lowest;
152 } finally {
153 listenersLock.writeLock().unlock();
154 }
155 }
156
157 public void updateListenerLevel(final Level status) {
158 if (status.intLevel() > listenersLevel) {
159 listenersLevel = status.intLevel();
160 }
161 }
162
163
164
165
166
167
168 public Iterable<StatusListener> getListeners() {
169 return listeners;
170 }
171
172
173
174
175 public void reset() {
176 listenersLock.writeLock().lock();
177 try {
178 for (final StatusListener listener : listeners) {
179 closeSilently(listener);
180 }
181 } finally {
182 listeners.clear();
183 listenersLock.writeLock().unlock();
184
185 clear();
186 }
187 }
188
189 private static void closeSilently(final Closeable resource) {
190 try {
191 resource.close();
192 } catch (final IOException ignored) {
193
194 }
195 }
196
197
198
199
200
201
202 public List<StatusData> getStatusData() {
203 msgLock.lock();
204 try {
205 return new ArrayList<>(messages);
206 } finally {
207 msgLock.unlock();
208 }
209 }
210
211
212
213
214 public void clear() {
215 msgLock.lock();
216 try {
217 messages.clear();
218 } finally {
219 msgLock.unlock();
220 }
221 }
222
223 @Override
224 public Level getLevel() {
225 return logger.getLevel();
226 }
227
228
229
230
231
232
233
234
235
236
237 @Override
238 public void logMessage(final String fqcn, final Level level, final Marker marker, final Message msg,
239 final Throwable t) {
240 StackTraceElement element = null;
241 if (fqcn != null) {
242 element = getStackTraceElement(fqcn, Thread.currentThread().getStackTrace());
243 }
244 final StatusData data = new StatusData(element, level, msg, t, null);
245 msgLock.lock();
246 try {
247 messages.add(data);
248 } finally {
249 msgLock.unlock();
250 }
251 if (listeners.size() > 0) {
252 for (final StatusListener listener : listeners) {
253 if (data.getLevel().isMoreSpecificThan(listener.getStatusLevel())) {
254 listener.log(data);
255 }
256 }
257 } else {
258 logger.logMessage(fqcn, level, marker, msg, t);
259 }
260 }
261
262 private StackTraceElement getStackTraceElement(final String fqcn, final StackTraceElement[] stackTrace) {
263 if (fqcn == null) {
264 return null;
265 }
266 boolean next = false;
267 for (final StackTraceElement element : stackTrace) {
268 final String className = element.getClassName();
269 if (next && !fqcn.equals(className)) {
270 return element;
271 }
272 if (fqcn.equals(className)) {
273 next = true;
274 } else if (NOT_AVAIL.equals(className)) {
275 break;
276 }
277 }
278 return null;
279 }
280
281 @Override
282 public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
283 return isEnabled(level, marker);
284 }
285
286 @Override
287 public boolean isEnabled(final Level level, final Marker marker, final String message) {
288 return isEnabled(level, marker);
289 }
290
291 @Override
292 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
293 return isEnabled(level, marker);
294 }
295
296 @Override
297 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) {
298 return isEnabled(level, marker);
299 }
300
301 @Override
302 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
303 final Object p1) {
304 return isEnabled(level, marker);
305 }
306
307 @Override
308 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
309 final Object p1, final Object p2) {
310 return isEnabled(level, marker);
311 }
312
313 @Override
314 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
315 final Object p1, final Object p2, final Object p3) {
316 return isEnabled(level, marker);
317 }
318
319 @Override
320 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
321 final Object p1, final Object p2, final Object p3,
322 final Object p4) {
323 return isEnabled(level, marker);
324 }
325
326 @Override
327 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
328 final Object p1, final Object p2, final Object p3,
329 final Object p4, final Object p5) {
330 return isEnabled(level, marker);
331 }
332
333 @Override
334 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
335 final Object p1, final Object p2, final Object p3,
336 final Object p4, final Object p5, final Object p6) {
337 return isEnabled(level, marker);
338 }
339
340 @Override
341 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
342 final Object p1, final Object p2, final Object p3,
343 final Object p4, final Object p5, final Object p6,
344 final Object p7) {
345 return isEnabled(level, marker);
346 }
347
348 @Override
349 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
350 final Object p1, final Object p2, final Object p3,
351 final Object p4, final Object p5, final Object p6,
352 final Object p7, final Object p8) {
353 return isEnabled(level, marker);
354 }
355
356 @Override
357 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
358 final Object p1, final Object p2, final Object p3,
359 final Object p4, final Object p5, final Object p6,
360 final Object p7, final Object p8, final Object p9) {
361 return isEnabled(level, marker);
362 }
363
364 @Override
365 public boolean isEnabled(final Level level, final Marker marker, final CharSequence message, final Throwable t) {
366 return isEnabled(level, marker);
367 }
368
369 @Override
370 public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
371 return isEnabled(level, marker);
372 }
373
374 @Override
375 public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
376 return isEnabled(level, marker);
377 }
378
379 @Override
380 public boolean isEnabled(final Level level, final Marker marker) {
381 if (listeners.size() > 0) {
382 return listenersLevel >= level.intLevel();
383 }
384 return logger.isEnabled(level, marker);
385 }
386
387
388
389
390
391
392 private class BoundedQueue<E> extends ConcurrentLinkedQueue<E> {
393
394 private static final long serialVersionUID = -3945953719763255337L;
395
396 private final int size;
397
398 BoundedQueue(final int size) {
399 this.size = size;
400 }
401
402 @Override
403 public boolean add(final E object) {
404 super.add(object);
405 while (messages.size() > size) {
406 messages.poll();
407 }
408 return size > 0;
409 }
410 }
411 }