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 org.apache.logging.log4j.simple.SimpleLogger;
20 import org.apache.logging.log4j.spi.AbstractLogger;
21 import org.apache.logging.log4j.Level;
22 import org.apache.logging.log4j.Marker;
23 import org.apache.logging.log4j.message.Message;
24 import org.apache.logging.log4j.util.PropertiesUtil;
25
26 import java.util.ArrayList;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Queue;
30 import java.util.concurrent.ConcurrentLinkedQueue;
31 import java.util.concurrent.CopyOnWriteArrayList;
32 import java.util.concurrent.locks.ReentrantLock;
33 import java.util.concurrent.locks.ReentrantReadWriteLock;
34
35
36
37
38 public final class StatusLogger extends AbstractLogger {
39
40
41
42
43
44 public static final String MAX_STATUS_ENTRIES = "log4j2.status.entries";
45
46 private static final String NOT_AVAIL = "?";
47
48 private static final PropertiesUtil PROPS = new PropertiesUtil("log4j2.StatusLogger.properties");
49
50 private static final int MAX_ENTRIES = PROPS.getIntegerProperty(MAX_STATUS_ENTRIES, 200);
51
52
53
54 private static final StatusLogger STATUS_LOGGER = new StatusLogger();
55
56 private final SimpleLogger logger;
57
58 private final CopyOnWriteArrayList<StatusListener> listeners = new CopyOnWriteArrayList<StatusListener>();
59 private final ReentrantReadWriteLock listenersLock = new ReentrantReadWriteLock();
60
61 private final Queue<StatusData> messages = new BoundedQueue<StatusData>(MAX_ENTRIES);
62 private final ReentrantLock msgLock = new ReentrantLock();
63
64 private StatusLogger() {
65 this.logger = new SimpleLogger("StatusLogger", Level.ERROR, false, true, false, false, "", null, PROPS,
66 System.err);
67 }
68
69
70
71
72
73 public static StatusLogger getLogger() {
74 return STATUS_LOGGER;
75 }
76
77 public void setLevel(final Level level) {
78 logger.setLevel(level);
79 }
80
81
82
83
84
85 public void registerListener(final StatusListener listener) {
86 listenersLock.writeLock().lock();
87 try {
88 listeners.add(listener);
89 } finally {
90 listenersLock.writeLock().unlock();
91 }
92 }
93
94
95
96
97
98 public void removeListener(final StatusListener listener) {
99 listenersLock.writeLock().lock();
100 try {
101 listeners.remove(listener);
102 } finally {
103 listenersLock.writeLock().unlock();
104 }
105 }
106
107
108
109
110
111 public Iterator<StatusListener> getListeners() {
112 return listeners.iterator();
113 }
114
115
116
117
118 public void reset() {
119 listeners.clear();
120 clear();
121 }
122
123
124
125
126
127 public List<StatusData> getStatusData() {
128 msgLock.lock();
129 try {
130 return new ArrayList<StatusData>(messages);
131 } finally {
132 msgLock.unlock();
133 }
134 }
135
136
137
138
139 public void clear() {
140 msgLock.lock();
141 try {
142 messages.clear();
143 } finally {
144 msgLock.unlock();
145 }
146 }
147
148
149
150
151
152
153
154
155
156
157 @Override
158 public void log(final Marker marker, final String fqcn, final Level level, final Message msg, final Throwable t) {
159 StackTraceElement element = null;
160 if (fqcn != null) {
161 element = getStackTraceElement(fqcn, Thread.currentThread().getStackTrace());
162 }
163 final StatusData data = new StatusData(element, level, msg, t);
164 msgLock.lock();
165 try {
166 messages.add(data);
167 } finally {
168 msgLock.unlock();
169 }
170 if (listeners.size() > 0) {
171 for (final StatusListener listener : listeners) {
172 listener.log(data);
173 }
174 } else {
175 logger.log(marker, fqcn, level, msg, t);
176 }
177 }
178
179 private StackTraceElement getStackTraceElement(final String fqcn, final StackTraceElement[] stackTrace) {
180 if (fqcn == null) {
181 return null;
182 }
183 boolean next = false;
184 for (final StackTraceElement element : stackTrace) {
185 if (next) {
186 return element;
187 }
188 final String className = element.getClassName();
189 if (fqcn.equals(className)) {
190 next = true;
191 } else if (NOT_AVAIL.equals(className)) {
192 break;
193 }
194 }
195 return null;
196 }
197
198 @Override
199 protected boolean isEnabled(final Level level, final Marker marker, final String data) {
200 return isEnabled(level, marker);
201 }
202
203 @Override
204 protected boolean isEnabled(final Level level, final Marker marker, final String data, final Throwable t) {
205 return isEnabled(level, marker);
206 }
207
208 @Override
209 protected boolean isEnabled(final Level level, final Marker marker, final String data, final Object... p1) {
210 return isEnabled(level, marker);
211 }
212
213 @Override
214 protected boolean isEnabled(final Level level, final Marker marker, final Object data, final Throwable t) {
215 return isEnabled(level, marker);
216 }
217
218 @Override
219 protected boolean isEnabled(final Level level, final Marker marker, final Message data, final Throwable t) {
220 return isEnabled(level, marker);
221 }
222
223 protected boolean isEnabled(final Level level, final Marker marker) {
224 if (listeners.size() > 0) {
225 return true;
226 }
227 switch (level) {
228 case FATAL:
229 return logger.isFatalEnabled(marker);
230 case TRACE:
231 return logger.isTraceEnabled(marker);
232 case DEBUG:
233 return logger.isDebugEnabled(marker);
234 case INFO:
235 return logger.isInfoEnabled(marker);
236 case WARN:
237 return logger.isWarnEnabled(marker);
238 case ERROR:
239 return logger.isErrorEnabled(marker);
240 }
241 return false;
242 }
243
244
245
246
247
248 private class BoundedQueue<E> extends ConcurrentLinkedQueue<E> {
249
250 private static final long serialVersionUID = -3945953719763255337L;
251
252 private final int size;
253
254 public BoundedQueue(final int size) {
255 this.size = size;
256 }
257
258 @Override
259 public boolean add(final E object) {
260 while (messages.size() > size) {
261 messages.poll();
262 }
263 return super.add(object);
264 }
265 }
266 }