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