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