1   /*
2    * Copyright (C) The Apache Software Foundation. All rights reserved.
3    *
4    * This software is published under the terms of the Apache Software
5    * License version 1.1, a copy of which has been included with this
6    * distribution in the LICENSE.txt file.  */
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  // Contributors:   Nelson Minar <nelson@monkey.org>
20  //                 Wolf Siberski
21  //                 Anders Kristensen <akristensen@dynamicsoft.com>
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&uuml;lc&uuml;
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   // Serialization
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); // use a tiny table
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     //vlads test patch.
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     //vlads test patch.
177     if (level.isGreaterOrEqual(Level.DEBUG)) {
178         this.throwable = new Throwable();
179     }
180     this.timeStamp = timeStamp;
181   }
182 
183   //vlads test patch.
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         // vlads test patch.
193         if (level.isGreaterOrEqual(Level.DEBUG)) {
194             locationInfo = new LocationInfo(this.throwable, fqnOfCategoryClass);
195             //locationInfo = new net.sf.jour.log4j.Java14LocationInfo(this.throwable, fqnOfCategoryClass);
196         } else {
197             locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass);
198             //locationInfo = new net.sf.jour.log4j.Java14LocationInfo(new Throwable(), fqnOfCategoryClass);
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     // Note the mdcCopy is used if it exists. Otherwise we use the MDC
268     // that is associated with the thread.
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       // the clone call is required for asynchronous logging.
287       // See also bug #5932.
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 	  // Note that we use Class.getDeclaredMethod instead of
368 	  // Class.getMethod. This assumes that the Level subclass
369 	  // implements the toLevel(int) method which is a
370 	  // requirement. Actually, it does not make sense for Level
371 	  // subclasses NOT to implement this method. Also note that
372 	  // only Level can be subclassed and not Priority.
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     // Make sure that no location info is available to Layouts
391     if(locationInfo == null)
392       locationInfo = new LocationInfo(null, null);
393   }
394 
395   private
396   void writeObject(ObjectOutputStream oos) throws java.io.IOException {
397     // Aside from returning the current thread name the wgetThreadName
398     // method sets the threadName variable.
399     this.getThreadName();
400 
401     // This sets the renders the message in case it wasn't up to now.
402     this.getRenderedMessage();
403 
404     // This call has a side effect of setting this.ndc and
405     // setting ndcLookupRequired to false if not already false.
406     this.getNDC();
407 
408     // This call has a side effect of setting this.mdcCopy and
409     // setting mdcLookupRequired to false if not already false.
410     this.getMDCCopy();
411 
412     // This sets the throwable sting representation of the event throwable.
413     this.getThrowableStrRep();
414 
415     oos.defaultWriteObject();
416 
417     // serialize this event's level
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       // writing directly the Class object would be nicer, except that
431       // serialized a Class object can not be read back by JDK
432       // 1.1.x. We have to resort to this hack instead.
433       oos.writeObject(clazz.getName());
434     }
435   }
436 
437 }