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