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