1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.pattern;
19
20 import org.apache.log4j.Category;
21 import org.apache.log4j.Level;
22 import org.apache.log4j.Logger;
23 import org.apache.log4j.MDC;
24 import org.apache.log4j.NDC;
25 import org.apache.log4j.Priority;
26 import org.apache.log4j.helpers.Loader;
27 import org.apache.log4j.helpers.LogLog;
28 import org.apache.log4j.spi.LocationInfo;
29 import org.apache.log4j.spi.LoggerRepository;
30 import org.apache.log4j.spi.RendererSupport;
31 import org.apache.log4j.spi.ThrowableInformation;
32
33 import java.io.ObjectInputStream;
34 import java.io.ObjectOutputStream;
35 import java.lang.reflect.Method;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.Hashtable;
39 import java.util.Map;
40 import java.util.Set;
41
42
43
44
45
46 /***
47 * This class is a copy of o.a.l.spi.LoggingEvent from
48 * log4j 1.2.15 which has been renamed to keep
49 * the same overall class name length to allow a
50 * serialization written with a prior instance of o.a.l.spi.LoggingEvent
51 * to be deserialized with this class just by mangling the class name
52 * in the byte stream.
53 *
54 */
55 public class LogEvent implements java.io.Serializable {
56
57 private static long startTime = System.currentTimeMillis();
58
59 /*** Fully qualified name of the calling category class. */
60 transient public final String fqnOfCategoryClass;
61
62 /***
63 * The category of the logging event. This field is not serialized
64 * for performance reasons.
65 *
66 * <p>It is set by the LoggingEvent constructor or set by a remote
67 * entity after deserialization.
68 *
69 * @deprecated This field will be marked as private or be completely
70 * removed in future releases. Please do not use it.
71 * */
72 transient private Category logger;
73
74 /***
75 * <p>The category (logger) name.
76 *
77 * @deprecated This field will be marked as private in future
78 * releases. Please do not access it directly. Use the {@link
79 * #getLoggerName} method instead.
80
81 * */
82 final public String categoryName;
83
84 /***
85 * Level of logging event. Level cannot be serializable because it
86 * is a flyweight. Due to its special seralization it cannot be
87 * declared final either.
88 *
89 * <p> This field should not be accessed directly. You shoud use the
90 * {@link #getLevel} method instead.
91 *
92 * @deprecated This field will be marked as private in future
93 * releases. Please do not access it directly. Use the {@link
94 * #getLevel} method instead.
95 * */
96 transient public Priority level;
97
98 /*** The nested diagnostic context (NDC) of logging event. */
99 private String ndc;
100
101 /*** The mapped diagnostic context (MDC) of logging event. */
102 private Hashtable mdcCopy;
103
104
105 /*** Have we tried to do an NDC lookup? If we did, there is no need
106 * to do it again. Note that its value is always false when
107 * serialized. Thus, a receiving SocketNode will never use it's own
108 * (incorrect) NDC. See also writeObject method. */
109 private boolean ndcLookupRequired = true;
110
111
112 /*** Have we tried to do an MDC lookup? If we did, there is no need
113 * to do it again. Note that its value is always false when
114 * serialized. See also the getMDC and getMDCCopy methods. */
115 private boolean mdcCopyLookupRequired = true;
116
117 /*** The application supplied message of logging event. */
118 transient private Object message;
119
120 /*** The application supplied message rendered through the log4j
121 objet rendering mechanism.*/
122 private String renderedMessage;
123
124 /*** The name of thread in which this logging event was generated. */
125 private String threadName;
126
127
128 /*** This
129 variable contains information about this event's throwable
130 */
131 private ThrowableInformation throwableInfo;
132
133 /*** The number of milliseconds elapsed from 1/1/1970 until logging event
134 was created. */
135 public final long timeStamp;
136 /*** Location information for the caller. */
137 private LocationInfo locationInfo;
138
139
140 static final long serialVersionUID = -868428216207166145L;
141
142 static final Integer[] PARAM_ARRAY = new Integer[1];
143 static final String TO_LEVEL = "toLevel";
144 static final Class[] TO_LEVEL_PARAMS = new Class[] {int.class};
145 static final Hashtable methodCache = new Hashtable(3);
146
147 /***
148 Instantiate a LoggingEvent from the supplied parameters.
149
150 <p>Except {@link #timeStamp} all the other fields of
151 <code>LoggingEvent</code> are filled when actually needed.
152 <p>
153 @param logger The logger generating this event.
154 @param level The level of this event.
155 @param message The message of this event.
156 @param throwable The throwable of this event. */
157 public LogEvent(String fqnOfCategoryClass, Category logger,
158 Priority level, Object message, Throwable throwable) {
159 this.fqnOfCategoryClass = fqnOfCategoryClass;
160 this.logger = logger;
161 this.categoryName = logger.getName();
162 this.level = level;
163 this.message = message;
164 if(throwable != null) {
165 this.throwableInfo = new ThrowableInformation(throwable);
166 }
167 timeStamp = System.currentTimeMillis();
168 }
169
170 /***
171 Instantiate a LoggingEvent from the supplied parameters.
172
173 <p>Except {@link #timeStamp} all the other fields of
174 <code>LoggingEvent</code> are filled when actually needed.
175 <p>
176 @param logger The logger generating this event.
177 @param timeStamp the timestamp of this logging event
178 @param level The level of this event.
179 @param message The message of this event.
180 @param throwable The throwable of this event. */
181 public LogEvent(String fqnOfCategoryClass, Category logger,
182 long timeStamp, Priority level, Object message,
183 Throwable throwable) {
184 this.fqnOfCategoryClass = fqnOfCategoryClass;
185 this.logger = logger;
186 this.categoryName = logger.getName();
187 this.level = level;
188 this.message = message;
189 if(throwable != null) {
190 this.throwableInfo = new ThrowableInformation(throwable);
191 }
192
193 this.timeStamp = timeStamp;
194 }
195
196 /***
197 Create new instance.
198 @since 1.2.15
199 @param fqnOfCategoryClass Fully qualified class name
200 of Logger implementation.
201 @param logger The logger generating this event.
202 @param timeStamp the timestamp of this logging event
203 @param level The level of this event.
204 @param message The message of this event.
205 @param threadName thread name
206 @param throwable The throwable of this event.
207 @param ndc Nested diagnostic context
208 @param info Location info
209 @param properties MDC properties
210 */
211 public LogEvent(final String fqnOfCategoryClass,
212 final Logger logger,
213 final long timeStamp,
214 final Level level,
215 final Object message,
216 final String threadName,
217 final ThrowableInformation throwable,
218 final String ndc,
219 final LocationInfo info,
220 final java.util.Map properties) {
221 super();
222 this.fqnOfCategoryClass = fqnOfCategoryClass;
223 this.logger = logger;
224 if (logger != null) {
225 categoryName = logger.getName();
226 } else {
227 categoryName = null;
228 }
229 this.level = level;
230 this.message = message;
231 if(throwable != null) {
232 this.throwableInfo = throwable;
233 }
234
235 this.timeStamp = timeStamp;
236 this.threadName = threadName;
237 ndcLookupRequired = false;
238 this.ndc = ndc;
239 this.locationInfo = info;
240 mdcCopyLookupRequired = false;
241 if (properties != null) {
242 mdcCopy = new java.util.Hashtable(properties);
243 }
244 }
245
246 /***
247 Set the location information for this logging event. The collected
248 information is cached for future use.
249 */
250 public LocationInfo getLocationInformation() {
251 if(locationInfo == null) {
252 locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass);
253 }
254 return locationInfo;
255 }
256
257 /***
258 * Return the level of this event. Use this form instead of directly
259 * accessing the <code>level</code> field. */
260 public Level getLevel() {
261 return (Level) level;
262 }
263
264 /***
265 * Return the name of the logger. Use this form instead of directly
266 * accessing the <code>categoryName</code> field.
267 */
268 public String getLoggerName() {
269 return categoryName;
270 }
271
272 /***
273 Return the message for this logging event.
274
275 <p>Before serialization, the returned object is the message
276 passed by the user to generate the logging event. After
277 serialization, the returned value equals the String form of the
278 message possibly after object rendering.
279
280 @since 1.1 */
281 public
282 Object getMessage() {
283 if(message != null) {
284 return message;
285 } else {
286 return getRenderedMessage();
287 }
288 }
289
290 /***
291 * This method returns the NDC for this event. It will return the
292 * correct content even if the event was generated in a different
293 * thread or even on a different machine. The {@link NDC#get} method
294 * should <em>never</em> be called directly. */
295 public
296 String getNDC() {
297 if(ndcLookupRequired) {
298 ndcLookupRequired = false;
299 ndc = NDC.get();
300 }
301 return ndc;
302 }
303
304
305 /***
306 Returns the the context corresponding to the <code>key</code>
307 parameter. If there is a local MDC copy, possibly because we are
308 in a logging server or running inside AsyncAppender, then we
309 search for the key in MDC copy, if a value is found it is
310 returned. Otherwise, if the search in MDC copy returns a null
311 result, then the current thread's <code>MDC</code> is used.
312
313 <p>Note that <em>both</em> the local MDC copy and the current
314 thread's MDC are searched.
315
316 */
317 public
318 Object getMDC(String key) {
319 Object r;
320
321
322 if(mdcCopy != null) {
323 r = mdcCopy.get(key);
324 if(r != null) {
325 return r;
326 }
327 }
328 return MDC.get(key);
329 }
330
331 /***
332 Obtain a copy of this thread's MDC prior to serialization or
333 asynchronous logging.
334 */
335 public
336 void getMDCCopy() {
337 if(mdcCopyLookupRequired) {
338 mdcCopyLookupRequired = false;
339
340
341 Hashtable t = (Hashtable) MDC.getContext();
342 if(t != null) {
343 mdcCopy = (Hashtable) t.clone();
344 }
345 }
346 }
347
348 public
349 String getRenderedMessage() {
350 if(renderedMessage == null && message != null) {
351 if(message instanceof String)
352 renderedMessage = (String) message;
353 else {
354 LoggerRepository repository = logger.getLoggerRepository();
355
356 if(repository instanceof RendererSupport) {
357 RendererSupport rs = (RendererSupport) repository;
358 renderedMessage= rs.getRendererMap().findAndRender(message);
359 } else {
360 renderedMessage = message.toString();
361 }
362 }
363 }
364 return renderedMessage;
365 }
366
367 /***
368 Returns the time when the application started, in milliseconds
369 elapsed since 01.01.1970. */
370 public static long getStartTime() {
371 return startTime;
372 }
373
374 public
375 String getThreadName() {
376 if(threadName == null)
377 threadName = (Thread.currentThread()).getName();
378 return threadName;
379 }
380
381 /***
382 Returns the throwable information contained within this
383 event. May be <code>null</code> if there is no such information.
384
385 <p>Note that the {@link Throwable} object contained within a
386 {@link ThrowableInformation} does not survive serialization.
387
388 @since 1.1 */
389 public
390 ThrowableInformation getThrowableInformation() {
391 return throwableInfo;
392 }
393
394 /***
395 Return this event's throwable's string[] representaion.
396 */
397 public
398 String[] getThrowableStrRep() {
399
400 if(throwableInfo == null)
401 return null;
402 else
403 return throwableInfo.getThrowableStrRep();
404 }
405
406
407 private
408 void readLevel(ObjectInputStream ois)
409 throws java.io.IOException, ClassNotFoundException {
410
411 int p = ois.readInt();
412 try {
413 String className = (String) ois.readObject();
414 if(className == null) {
415 level = Level.toLevel(p);
416 } else {
417 Method m = (Method) methodCache.get(className);
418 if(m == null) {
419 Class clazz = Loader.loadClass(className);
420
421
422
423
424
425
426 m = clazz.getDeclaredMethod(TO_LEVEL, TO_LEVEL_PARAMS);
427 methodCache.put(className, m);
428 }
429 PARAM_ARRAY[0] = new Integer(p);
430 level = (Level) m.invoke(null, PARAM_ARRAY);
431 }
432 } catch(Exception e) {
433 LogLog.warn("Level deserialization failed, reverting to default.", e);
434 level = Level.toLevel(p);
435 }
436 }
437
438 private void readObject(ObjectInputStream ois)
439 throws java.io.IOException, ClassNotFoundException {
440 ois.defaultReadObject();
441 readLevel(ois);
442
443
444 if(locationInfo == null)
445 locationInfo = new LocationInfo(null, null);
446 }
447
448 private
449 void writeObject(ObjectOutputStream oos) throws java.io.IOException {
450
451
452 this.getThreadName();
453
454
455 this.getRenderedMessage();
456
457
458
459 this.getNDC();
460
461
462
463 this.getMDCCopy();
464
465
466 this.getThrowableStrRep();
467
468 oos.defaultWriteObject();
469
470
471 writeLevel(oos);
472 }
473
474 private
475 void writeLevel(ObjectOutputStream oos) throws java.io.IOException {
476
477 oos.writeInt(level.toInt());
478
479 Class clazz = level.getClass();
480 if(clazz == Level.class) {
481 oos.writeObject(null);
482 } else {
483
484
485
486 oos.writeObject(clazz.getName());
487 }
488 }
489
490 /***
491 * Set value for MDC property.
492 * This adds the specified MDC property to the event.
493 * Access to the MDC is not synchronized, so this
494 * method should only be called when it is known that
495 * no other threads are accessing the MDC.
496 * @since 1.2.15
497 * @param propName
498 * @param propValue
499 */
500 public final void setProperty(final String propName,
501 final String propValue) {
502 if (mdcCopy == null) {
503 getMDCCopy();
504 }
505 if (mdcCopy == null) {
506 mdcCopy = new Hashtable();
507 }
508 mdcCopy.put(propName, propValue);
509 }
510
511 /***
512 * Return a property for this event. The return value can be null.
513 *
514 * Equivalent to getMDC(String) in log4j 1.2. Provided
515 * for compatibility with log4j 1.3.
516 *
517 * @param key property name
518 * @return property value or null if property not set
519 * @since 1.2.15
520 */
521 public final String getProperty(final String key) {
522 Object value = getMDC(key);
523 String retval = null;
524 if (value != null) {
525 retval = value.toString();
526 }
527 return retval;
528 }
529
530 /***
531 * Check for the existence of location information without creating it
532 * (a byproduct of calling getLocationInformation).
533 * @return true if location information has been extracted.
534 * @since 1.2.15
535 */
536 public final boolean locationInformationExists() {
537 return (locationInfo != null);
538 }
539
540 /***
541 * Getter for the event's time stamp. The time stamp is calculated starting
542 * from 1970-01-01 GMT.
543 * @return timestamp
544 *
545 * @since 1.2.15
546 */
547 public final long getTimeStamp() {
548 return timeStamp;
549 }
550
551 /***
552 * Returns the set of the key values in the properties
553 * for the event.
554 *
555 * The returned set is unmodifiable by the caller.
556 *
557 * Provided for compatibility with log4j 1.3
558 *
559 * @return Set an unmodifiable set of the property keys.
560 * @since 1.2.15
561 */
562 public Set getPropertyKeySet() {
563 return getProperties().keySet();
564 }
565
566 /***
567 * Returns the set of properties
568 * for the event.
569 *
570 * The returned set is unmodifiable by the caller.
571 *
572 * Provided for compatibility with log4j 1.3
573 *
574 * @return Set an unmodifiable map of the properties.
575 * @since 1.2.15
576 */
577 public Map getProperties() {
578 getMDCCopy();
579 Map properties;
580 if (mdcCopy == null) {
581 properties = new HashMap();
582 } else {
583 properties = mdcCopy;
584 }
585 return Collections.unmodifiableMap(properties);
586 }
587
588 /***
589 * Get the fully qualified name of the calling logger sub-class/wrapper.
590 * Provided for compatibility with log4j 1.3
591 * @return fully qualified class name, may be null.
592 * @since 1.2.15
593 */
594 public String getFQNOfLoggerClass() {
595 return fqnOfCategoryClass;
596 }
597
598
599
600 }