1
2
3
4
5
6
7
8 package org.apache.log4j.spi;
9
10 import org.apache.log4j.*;
11
12 import org.apache.log4j.helpers.LogLog;
13 import org.apache.log4j.helpers.Loader;
14 import java.lang.reflect.Method;
15 import java.io.ObjectOutputStream;
16 import java.io.ObjectInputStream;
17 import java.util.Hashtable;
18
19
20
21
22
23 /***
24 The internal representation of logging events. When an affirmative
25 decision is made to log then a <code>LoggingEvent</code> instance
26 is created. This instance is passed around to the different log4j
27 components.
28
29 <p>This class is of concern to those wishing to extend log4j.
30
31 @author Ceki Gülcü
32 @author James P. Cakalic
33
34 @since 0.8.2 */
35 public class LoggingEvent implements java.io.Serializable {
36
37 private static long startTime = System.currentTimeMillis();
38
39 /*** Fully qualified name of the calling category class. */
40 transient public final String fqnOfCategoryClass;
41
42 /***
43 * The category of the logging event. This field is not serialized
44 * for performance reasons.
45 *
46 * <p>It is set by the LoggingEvent constructor or set by a remote
47 * entity after deserialization.
48 *
49 * @deprecated This field will be marked as private or be completely
50 * removed in future releases. Please do not use it.
51 * */
52 transient private Category logger;
53
54 /***
55 * <p>The category (logger) name.
56 *
57 * @deprecated This field will be marked as private in future
58 * releases. Please do not access it directly. Use the {@link
59 * #getLoggerName} method instead.
60
61 * */
62 final public String categoryName;
63
64 /***
65 * Level of logging event. Level cannot be serializable because it
66 * is a flyweight. Due to its special seralization it cannot be
67 * declared final either.
68 *
69 * <p> This field should not be accessed directly. You shoud use the
70 * {@link #getLevel} method instead.
71 *
72 * @deprecated This field will be marked as private in future
73 * releases. Please do not access it directly. Use the {@link
74 * #getLevel} method instead.
75 * */
76 transient public Priority level;
77
78 /*** The nested diagnostic context (NDC) of logging event. */
79 private String ndc;
80
81 /*** The mapped diagnostic context (MDC) of logging event. */
82 private Hashtable mdcCopy;
83
84
85 /*** Have we tried to do an NDC lookup? If we did, there is no need
86 * to do it again. Note that its value is always false when
87 * serialized. Thus, a receiving SocketNode will never use it's own
88 * (incorrect) NDC. See also writeObject method. */
89 private boolean ndcLookupRequired = true;
90
91
92 /*** Have we tried to do an MDC lookup? If we did, there is no need
93 * to do it again. Note that its value is always false when
94 * serialized. See also the getMDC and getMDCCopy methods. */
95 private boolean mdcCopyLookupRequired = true;
96
97 /*** The application supplied message of logging event. */
98 transient private Object message;
99
100 /*** The application supplied message rendered through the log4j
101 objet rendering mechanism.*/
102 private String renderedMessage;
103
104 /*** The name of thread in which this logging event was generated. */
105 private String threadName;
106
107
108 /*** This
109 variable contains information about this event's throwable
110 */
111 private ThrowableInformation throwableInfo;
112
113 /*** The number of milliseconds elapsed from 1/1/1970 until logging event
114 was created. */
115 public final long timeStamp;
116 /*** Location information for the caller. */
117 private LocationInfo locationInfo;
118
119
120 static final long serialVersionUID = -868428216207166145L;
121
122 static final Integer[] PARAM_ARRAY = new Integer[1];
123 static final String TO_LEVEL = "toLevel";
124 static final Class[] TO_LEVEL_PARAMS = new Class[] {int.class};
125 static final Hashtable methodCache = new Hashtable(3);
126
127 /***
128 Instantiate a LoggingEvent from the supplied parameters.
129
130 <p>Except {@link #timeStamp} all the other fields of
131 <code>LoggingEvent</code> are filled when actually needed.
132 <p>
133 @param logger The logger generating this event.
134 @param level The level of this event.
135 @param message The message of this event.
136 @param throwable The throwable of this event. */
137 public LoggingEvent(String fqnOfCategoryClass, Category logger,
138 Priority level, Object message, Throwable throwable) {
139 this.fqnOfCategoryClass = fqnOfCategoryClass;
140 this.logger = logger;
141 this.categoryName = logger.getName();
142 this.level = level;
143 this.message = message;
144 if(throwable != null) {
145 this.throwableInfo = new ThrowableInformation(throwable);
146 }
147
148 if (level.isGreaterOrEqual(Level.DEBUG)) {
149 this.throwable = new Throwable();
150 }
151 timeStamp = System.currentTimeMillis();
152 }
153
154 /***
155 Instantiate a LoggingEvent from the supplied parameters.
156
157 <p>Except {@link #timeStamp} all the other fields of
158 <code>LoggingEvent</code> are filled when actually needed.
159 <p>
160 @param logger The logger generating this event.
161 @param timeStamp the timestamp of this logging event
162 @param level The level of this event.
163 @param message The message of this event.
164 @param throwable The throwable of this event. */
165 public LoggingEvent(String fqnOfCategoryClass, Category logger,
166 long timeStamp, Priority level, Object message,
167 Throwable throwable) {
168 this.fqnOfCategoryClass = fqnOfCategoryClass;
169 this.logger = logger;
170 this.categoryName = logger.getName();
171 this.level = level;
172 this.message = message;
173 if(throwable != null) {
174 this.throwableInfo = new ThrowableInformation(throwable);
175 }
176
177 if (level.isGreaterOrEqual(Level.DEBUG)) {
178 this.throwable = new Throwable();
179 }
180 this.timeStamp = timeStamp;
181 }
182
183
184 transient private Throwable throwable;
185
186 /***
187 Set the location information for this logging event. The collected
188 information is cached for future use.
189 */
190 public LocationInfo getLocationInformation() {
191 if(locationInfo == null) {
192
193 if (level.isGreaterOrEqual(Level.DEBUG)) {
194 locationInfo = new LocationInfo(this.throwable, fqnOfCategoryClass);
195
196 } else {
197 locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass);
198
199 }
200 }
201 return locationInfo;
202 }
203
204 /***
205 * Return the level of this event. Use this form instead of directly
206 * accessing the <code>level</code> field. */
207 public Level getLevel() {
208 return (Level) level;
209 }
210
211 /***
212 * Return the name of the logger. Use this form instead of directly
213 * accessing the <code>categoryName</code> field.
214 */
215 public String getLoggerName() {
216 return categoryName;
217 }
218
219 /***
220 Return the message for this logging event.
221
222 <p>Before serialization, the returned object is the message
223 passed by the user to generate the logging event. After
224 serialization, the returned value equals the String form of the
225 message possibly after object rendering.
226
227 @since 1.1 */
228 public
229 Object getMessage() {
230 if(message != null) {
231 return message;
232 } else {
233 return getRenderedMessage();
234 }
235 }
236
237 /***
238 * This method returns the NDC for this event. It will return the
239 * correct content even if the event was generated in a different
240 * thread or even on a different machine. The {@link NDC#get} method
241 * should <em>never</em> be called directly. */
242 public
243 String getNDC() {
244 if(ndcLookupRequired) {
245 ndcLookupRequired = false;
246 ndc = NDC.get();
247 }
248 return ndc;
249 }
250
251
252 /***
253 Returns the the context corresponding to the <code>key</code>
254 parameter. If there is a local MDC copy, possibly because we are
255 in a logging server or running inside AsyncAppender, then we
256 search for the key in MDC copy, if a value is found it is
257 returned. Otherwise, if the search in MDC copy returns a null
258 result, then the current thread's <code>MDC</code> is used.
259
260 <p>Note that <em>both</em> the local MDC copy and the current
261 thread's MDC are searched.
262
263 */
264 public
265 Object getMDC(String key) {
266 Object r;
267
268
269 if(mdcCopy != null) {
270 r = mdcCopy.get(key);
271 if(r != null) {
272 return r;
273 }
274 }
275 return MDC.get(key);
276 }
277
278 /***
279 Obtain a copy of this thread's MDC prior to serialization or
280 asynchronous logging.
281 */
282 public
283 void getMDCCopy() {
284 if(mdcCopyLookupRequired) {
285 mdcCopyLookupRequired = false;
286
287
288 Hashtable t = (Hashtable) MDC.getContext();
289 if(t != null) {
290 mdcCopy = (Hashtable) t.clone();
291 }
292 }
293 }
294
295 public
296 String getRenderedMessage() {
297 if(renderedMessage == null && message != null) {
298 if(message instanceof String)
299 renderedMessage = (String) message;
300 else {
301 LoggerRepository repository = logger.getLoggerRepository();
302
303 if(repository instanceof RendererSupport) {
304 RendererSupport rs = (RendererSupport) repository;
305 renderedMessage= rs.getRendererMap().findAndRender(message);
306 } else {
307 renderedMessage = message.toString();
308 }
309 }
310 }
311 return renderedMessage;
312 }
313
314 /***
315 Returns the time when the application started, in milliseconds
316 elapsed since 01.01.1970. */
317 public static long getStartTime() {
318 return startTime;
319 }
320
321 public
322 String getThreadName() {
323 if(threadName == null)
324 threadName = (Thread.currentThread()).getName();
325 return threadName;
326 }
327
328 /***
329 Returns the throwable information contained within this
330 event. May be <code>null</code> if there is no such information.
331
332 <p>Note that the {@link Throwable} object contained within a
333 {@link ThrowableInformation} does not survive serialization.
334
335 @since 1.1 */
336 public
337 ThrowableInformation getThrowableInformation() {
338 return throwableInfo;
339 }
340
341 /***
342 Return this event's throwable's string[] representaion.
343 */
344 public
345 String[] getThrowableStrRep() {
346
347 if(throwableInfo == null)
348 return null;
349 else
350 return throwableInfo.getThrowableStrRep();
351 }
352
353
354 private
355 void readLevel(ObjectInputStream ois)
356 throws java.io.IOException, ClassNotFoundException {
357
358 int p = ois.readInt();
359 try {
360 String className = (String) ois.readObject();
361 if(className == null) {
362 level = Level.toLevel(p);
363 } else {
364 Method m = (Method) methodCache.get(className);
365 if(m == null) {
366 Class clazz = Loader.loadClass(className);
367
368
369
370
371
372
373 m = clazz.getDeclaredMethod(TO_LEVEL, TO_LEVEL_PARAMS);
374 methodCache.put(className, m);
375 }
376 PARAM_ARRAY[0] = new Integer(p);
377 level = (Level) m.invoke(null, PARAM_ARRAY);
378 }
379 } catch(Exception e) {
380 LogLog.warn("Level deserialization failed, reverting to default.", e);
381 level = Level.toLevel(p);
382 }
383 }
384
385 private void readObject(ObjectInputStream ois)
386 throws java.io.IOException, ClassNotFoundException {
387 ois.defaultReadObject();
388 readLevel(ois);
389
390
391 if(locationInfo == null)
392 locationInfo = new LocationInfo(null, null);
393 }
394
395 private
396 void writeObject(ObjectOutputStream oos) throws java.io.IOException {
397
398
399 this.getThreadName();
400
401
402 this.getRenderedMessage();
403
404
405
406 this.getNDC();
407
408
409
410 this.getMDCCopy();
411
412
413 this.getThrowableStrRep();
414
415 oos.defaultWriteObject();
416
417
418 writeLevel(oos);
419 }
420
421 private
422 void writeLevel(ObjectOutputStream oos) throws java.io.IOException {
423
424 oos.writeInt(level.toInt());
425
426 Class clazz = level.getClass();
427 if(clazz == Level.class) {
428 oos.writeObject(null);
429 } else {
430
431
432
433 oos.writeObject(clazz.getName());
434 }
435 }
436
437 }