1
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
53 public final class StatusLogger extends AbstractLogger {
54
55
59 public static final String MAX_STATUS_ENTRIES = "log4j2.status.entries";
60
61
65 public static final String DEFAULT_STATUS_LISTENER_LEVEL = "log4j2.StatusLogger.level";
66
67
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
132 private StatusLogger(final String name, final MessageFactory messageFactory) {
133 super(name, messageFactory);
134 final String dateFormat = PROPS.getStringProperty(STATUS_DATE_FORMAT, Strings.EMPTY);
135 final boolean showDateTime = !Strings.isEmpty(dateFormat);
136 final Level loggerLevel = isDebugPropertyEnabled() ? Level.TRACE : Level.ERROR;
137 this.logger = new SimpleLogger("StatusLogger", loggerLevel, false, true, showDateTime, false, dateFormat, messageFactory, PROPS, System.err);
138 this.listenersLevel = Level.toLevel(DEFAULT_STATUS_LEVEL, Level.WARN).intLevel();
139 }
140
141
142 private boolean isDebugPropertyEnabled() {
143 return PropertiesUtil.getProperties().getBooleanProperty(Constants.LOG4J2_DEBUG, false, true);
144 }
145
146
151 public static StatusLogger getLogger() {
152 return STATUS_LOGGER;
153 }
154
155 public void setLevel(final Level level) {
156 logger.setLevel(level);
157 }
158
159
164 public void registerListener(final StatusListener listener) {
165 listenersLock.writeLock().lock();
166 try {
167 listeners.add(listener);
168 final Level lvl = listener.getStatusLevel();
169 if (listenersLevel < lvl.intLevel()) {
170 listenersLevel = lvl.intLevel();
171 }
172 } finally {
173 listenersLock.writeLock().unlock();
174 }
175 }
176
177
182 public void removeListener(final StatusListener listener) {
183 closeSilently(listener);
184 listenersLock.writeLock().lock();
185 try {
186 listeners.remove(listener);
187 int lowest = Level.toLevel(DEFAULT_STATUS_LEVEL, Level.WARN).intLevel();
188 for (final StatusListener statusListener : listeners) {
189 final int level = statusListener.getStatusLevel().intLevel();
190 if (lowest < level) {
191 lowest = level;
192 }
193 }
194 listenersLevel = lowest;
195 } finally {
196 listenersLock.writeLock().unlock();
197 }
198 }
199
200 public void updateListenerLevel(final Level status) {
201 if (status.intLevel() > listenersLevel) {
202 listenersLevel = status.intLevel();
203 }
204 }
205
206
211 public Iterable<StatusListener> getListeners() {
212 return listeners;
213 }
214
215
218 public void reset() {
219 listenersLock.writeLock().lock();
220 try {
221 for (final StatusListener listener : listeners) {
222 closeSilently(listener);
223 }
224 } finally {
225 listeners.clear();
226 listenersLock.writeLock().unlock();
227
228 clear();
229 }
230 }
231
232 private static void closeSilently(final Closeable resource) {
233 try {
234 resource.close();
235 } catch (final IOException ignored) {
236
237 }
238 }
239
240
245 public List<StatusData> getStatusData() {
246 msgLock.lock();
247 try {
248 return new ArrayList<>(messages);
249 } finally {
250 msgLock.unlock();
251 }
252 }
253
254
257 public void clear() {
258 msgLock.lock();
259 try {
260 messages.clear();
261 } finally {
262 msgLock.unlock();
263 }
264 }
265
266 @Override
267 public Level getLevel() {
268 return logger.getLevel();
269 }
270
271
280 @Override
281 public void logMessage(final String fqcn, final Level level, final Marker marker, final Message msg,
282 final Throwable t) {
283 StackTraceElement element = null;
284 if (fqcn != null) {
285 element = getStackTraceElement(fqcn, Thread.currentThread().getStackTrace());
286 }
287 final StatusData data = new StatusData(element, level, msg, t, null);
288 msgLock.lock();
289 try {
290 messages.add(data);
291 } finally {
292 msgLock.unlock();
293 }
294
295 if (isDebugPropertyEnabled() || (listeners.size() <= 0)) {
296 logger.logMessage(fqcn, level, marker, msg, t);
297 } else {
298 for (final StatusListener listener : listeners) {
299 if (data.getLevel().isMoreSpecificThan(listener.getStatusLevel())) {
300 listener.log(data);
301 }
302 }
303 }
304 }
305
306 private StackTraceElement getStackTraceElement(final String fqcn, final StackTraceElement[] stackTrace) {
307 if (fqcn == null) {
308 return null;
309 }
310 boolean next = false;
311 for (final StackTraceElement element : stackTrace) {
312 final String className = element.getClassName();
313 if (next && !fqcn.equals(className)) {
314 return element;
315 }
316 if (fqcn.equals(className)) {
317 next = true;
318 } else if (NOT_AVAIL.equals(className)) {
319 break;
320 }
321 }
322 return null;
323 }
324
325 @Override
326 public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
327 return isEnabled(level, marker);
328 }
329
330 @Override
331 public boolean isEnabled(final Level level, final Marker marker, final String message) {
332 return isEnabled(level, marker);
333 }
334
335 @Override
336 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
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 return isEnabled(level, marker);
343 }
344
345 @Override
346 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
347 final Object p1) {
348 return isEnabled(level, marker);
349 }
350
351 @Override
352 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
353 final Object p1, final Object p2) {
354 return isEnabled(level, marker);
355 }
356
357 @Override
358 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
359 final Object p1, final Object p2, final Object p3) {
360 return isEnabled(level, marker);
361 }
362
363 @Override
364 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
365 final Object p1, final Object p2, final Object p3,
366 final Object p4) {
367 return isEnabled(level, marker);
368 }
369
370 @Override
371 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
372 final Object p1, final Object p2, final Object p3,
373 final Object p4, final Object p5) {
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 return isEnabled(level, marker);
382 }
383
384 @Override
385 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
386 final Object p1, final Object p2, final Object p3,
387 final Object p4, final Object p5, final Object p6,
388 final Object p7) {
389 return isEnabled(level, marker);
390 }
391
392 @Override
393 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
394 final Object p1, final Object p2, final Object p3,
395 final Object p4, final Object p5, final Object p6,
396 final Object p7, final Object p8) {
397 return isEnabled(level, marker);
398 }
399
400 @Override
401 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
402 final Object p1, final Object p2, final Object p3,
403 final Object p4, final Object p5, final Object p6,
404 final Object p7, final Object p8, final Object p9) {
405 return isEnabled(level, marker);
406 }
407
408 @Override
409 public boolean isEnabled(final Level level, final Marker marker, final CharSequence message, final Throwable t) {
410 return isEnabled(level, marker);
411 }
412
413 @Override
414 public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
415 return isEnabled(level, marker);
416 }
417
418 @Override
419 public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
420 return isEnabled(level, marker);
421 }
422
423 @Override
424 public boolean isEnabled(final Level level, final Marker marker) {
425
426 if (isDebugPropertyEnabled()) {
427 return true;
428 }
429 if (listeners.size() > 0) {
430 return listenersLevel >= level.intLevel();
431 }
432 return logger.isEnabled(level, marker);
433 }
434
435
440 private class BoundedQueue<E> extends ConcurrentLinkedQueue<E> {
441
442 private static final long serialVersionUID = -3945953719763255337L;
443
444 private final int size;
445
446 BoundedQueue(final int size) {
447 this.size = size;
448 }
449
450 @Override
451 public boolean add(final E object) {
452 super.add(object);
453 while (messages.size() > size) {
454 messages.poll();
455 }
456 return size > 0;
457 }
458 }
459 }
460