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 Level getLevel() {
78 return logger.getLevel();
79 }
80
81 public void setLevel(final Level level) {
82 logger.setLevel(level);
83 }
84
85
86
87
88
89 public void registerListener(final StatusListener listener) {
90 listenersLock.writeLock().lock();
91 try {
92 listeners.add(listener);
93 } finally {
94 listenersLock.writeLock().unlock();
95 }
96 }
97
98
99
100
101
102 public void removeListener(final StatusListener listener) {
103 listenersLock.writeLock().lock();
104 try {
105 listeners.remove(listener);
106 } finally {
107 listenersLock.writeLock().unlock();
108 }
109 }
110
111
112
113
114
115 public Iterator<StatusListener> getListeners() {
116 return listeners.iterator();
117 }
118
119
120
121
122 public void reset() {
123 listeners.clear();
124 clear();
125 }
126
127
128
129
130
131 public List<StatusData> getStatusData() {
132 msgLock.lock();
133 try {
134 return new ArrayList<StatusData>(messages);
135 } finally {
136 msgLock.unlock();
137 }
138 }
139
140
141
142
143 public void clear() {
144 msgLock.lock();
145 try {
146 messages.clear();
147 } finally {
148 msgLock.unlock();
149 }
150 }
151
152
153
154
155
156
157
158
159
160
161 @Override
162 public void log(final Marker marker, final String fqcn, final Level level, final Message msg, final Throwable t) {
163 StackTraceElement element = null;
164 if (fqcn != null) {
165 element = getStackTraceElement(fqcn, Thread.currentThread().getStackTrace());
166 }
167 final StatusData data = new StatusData(element, level, msg, t);
168 msgLock.lock();
169 try {
170 messages.add(data);
171 } finally {
172 msgLock.unlock();
173 }
174 if (listeners.size() > 0) {
175 for (final StatusListener listener : listeners) {
176 listener.log(data);
177 }
178 } else {
179 logger.log(marker, fqcn, level, msg, t);
180 }
181 }
182
183 private StackTraceElement getStackTraceElement(final String fqcn, final StackTraceElement[] stackTrace) {
184 if (fqcn == null) {
185 return null;
186 }
187 boolean next = false;
188 for (final StackTraceElement element : stackTrace) {
189 if (next) {
190 return element;
191 }
192 final String className = element.getClassName();
193 if (fqcn.equals(className)) {
194 next = true;
195 } else if (NOT_AVAIL.equals(className)) {
196 break;
197 }
198 }
199 return null;
200 }
201
202 @Override
203 protected boolean isEnabled(final Level level, final Marker marker, final String data) {
204 return isEnabled(level, marker);
205 }
206
207 @Override
208 protected boolean isEnabled(final Level level, final Marker marker, final String data, final Throwable t) {
209 return isEnabled(level, marker);
210 }
211
212 @Override
213 protected boolean isEnabled(final Level level, final Marker marker, final String data, final Object... p1) {
214 return isEnabled(level, marker);
215 }
216
217 @Override
218 protected boolean isEnabled(final Level level, final Marker marker, final Object data, final Throwable t) {
219 return isEnabled(level, marker);
220 }
221
222 @Override
223 protected boolean isEnabled(final Level level, final Marker marker, final Message data, final Throwable t) {
224 return isEnabled(level, marker);
225 }
226
227 @Override
228 public boolean isEnabled(final Level level, final Marker marker) {
229 if (listeners.size() > 0) {
230 return true;
231 }
232 switch (level) {
233 case FATAL:
234 return logger.isFatalEnabled(marker);
235 case TRACE:
236 return logger.isTraceEnabled(marker);
237 case DEBUG:
238 return logger.isDebugEnabled(marker);
239 case INFO:
240 return logger.isInfoEnabled(marker);
241 case WARN:
242 return logger.isWarnEnabled(marker);
243 case ERROR:
244 return logger.isErrorEnabled(marker);
245 }
246 return false;
247 }
248
249
250
251
252
253 private class BoundedQueue<E> extends ConcurrentLinkedQueue<E> {
254
255 private static final long serialVersionUID = -3945953719763255337L;
256
257 private final int size;
258
259 public BoundedQueue(final int size) {
260 this.size = size;
261 }
262
263 @Override
264 public boolean add(final E object) {
265 while (messages.size() > size) {
266 messages.poll();
267 }
268 return super.add(object);
269 }
270 }
271 }