0475b671affe2708bd89a0b858e3e5ae98cdfc7b
[jalview.git] / srcjar2 / org / apache / log4j / spi / LoggingEvent.java
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  * 
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  * 
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package org.apache.log4j.spi;
19
20 import java.io.InterruptedIOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Hashtable;
28 import java.util.Map;
29 import java.util.Set;
30
31 import org.apache.log4j.Category;
32 import org.apache.log4j.Level;
33 import org.apache.log4j.MDC;
34 import org.apache.log4j.NDC;
35 import org.apache.log4j.Priority;
36 import org.apache.log4j.helpers.Loader;
37 import org.apache.log4j.helpers.LogLog;
38
39 // Contributors:   Nelson Minar <nelson@monkey.org>
40 //                 Wolf Siberski
41 //                 Anders Kristensen <akristensen@dynamicsoft.com>
42
43 /**
44    The internal representation of logging events. When an affirmative
45    decision is made to log then a <code>LoggingEvent</code> instance
46    is created. This instance is passed around to the different log4j
47    components.
48
49    <p>This class is of concern to those wishing to extend log4j.
50
51    @author Ceki G&uuml;lc&uuml;
52    @author James P. Cakalic
53
54    @since 0.8.2 */
55 public class LoggingEvent 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   // Serialization
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); // use a tiny table
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 LoggingEvent(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, logger);
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 LoggingEvent(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, logger);
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 LoggingEvent(final String fqnOfCategoryClass,
212                         final Category 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   /**
248      Set the location information for this logging event. The collected
249      information is cached for future use.
250    */
251   public LocationInfo getLocationInformation() {
252     if(locationInfo == null) {
253       locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass);
254     }
255     return locationInfo;
256   }
257
258   /**
259    * Return the level of this event. Use this form instead of directly
260    * accessing the <code>level</code> field.  */
261   public Level getLevel() {
262     return (Level) level;
263   }
264
265   /**
266    * Return the name of the logger. Use this form instead of directly
267    * accessing the <code>categoryName</code> field.  
268    */
269   public String getLoggerName() {
270     return categoryName;
271   }
272
273     /**
274      * Gets the logger of the event.
275      * Use should be restricted to cloning events.
276      * @since 1.2.15
277      */
278     public Category getLogger() {
279       return logger;
280     }
281
282   /**
283      Return the message for this logging event.
284
285      <p>Before serialization, the returned object is the message
286      passed by the user to generate the logging event. After
287      serialization, the returned value equals the String form of the
288      message possibly after object rendering.
289
290      @since 1.1 */
291   public
292   Object getMessage() {
293     if(message != null) {
294       return message;
295     } else {
296       return getRenderedMessage();
297     }
298   }
299
300   /**
301    * This method returns the NDC for this event. It will return the
302    * correct content even if the event was generated in a different
303    * thread or even on a different machine. The {@link NDC#get} method
304    * should <em>never</em> be called directly.  */
305   public
306   String getNDC() {
307     if(ndcLookupRequired) {
308       ndcLookupRequired = false;
309       ndc = NDC.get();
310     }
311     return ndc;
312   }
313
314
315   /**
316       Returns the the context corresponding to the <code>key</code>
317       parameter. If there is a local MDC copy, possibly because we are
318       in a logging server or running inside AsyncAppender, then we
319       search for the key in MDC copy, if a value is found it is
320       returned. Otherwise, if the search in MDC copy returns a null
321       result, then the current thread's <code>MDC</code> is used.
322       
323       <p>Note that <em>both</em> the local MDC copy and the current
324       thread's MDC are searched.
325
326   */
327   public
328   Object getMDC(String key) {
329     Object r;
330     // Note the mdcCopy is used if it exists. Otherwise we use the MDC
331     // that is associated with the thread.
332     if(mdcCopy != null) {
333       r = mdcCopy.get(key);
334       if(r != null) {
335         return r;
336       }
337     }
338     return MDC.get(key);
339   }
340
341   /**
342      Obtain a copy of this thread's MDC prior to serialization or
343      asynchronous logging.  
344   */
345   public
346   void getMDCCopy() {
347     if(mdcCopyLookupRequired) {
348       mdcCopyLookupRequired = false;
349       // the clone call is required for asynchronous logging.
350       // See also bug #5932.
351       Hashtable t = MDC.getContext();
352       if(t != null) {
353         mdcCopy = (Hashtable) t.clone();
354       }
355     }
356   }
357
358   public
359   String getRenderedMessage() {
360      if(renderedMessage == null && message != null) {
361        if(message instanceof String) {
362         renderedMessage = (String) message;
363     } else {
364          LoggerRepository repository = logger.getLoggerRepository();
365
366          if(repository instanceof RendererSupport) {
367            RendererSupport rs = (RendererSupport) repository;
368            renderedMessage= rs.getRendererMap().findAndRender(message);
369          } else {
370            renderedMessage = message.toString();
371          }
372        }
373      }
374      return renderedMessage;
375   }
376
377   /**
378      Returns the time when the application started, in milliseconds
379      elapsed since 01.01.1970.  */
380   public static long getStartTime() {
381     return startTime;
382   }
383
384   public
385   String getThreadName() {
386     if(threadName == null) {
387         threadName = (Thread.currentThread()).getName();
388     }
389     return threadName;
390   }
391
392   /**
393      Returns the throwable information contained within this
394      event. May be <code>null</code> if there is no such information.
395
396      <p>Note that the {@link Throwable} object contained within a
397      {@link ThrowableInformation} does not survive serialization.
398
399      @since 1.1 */
400   public
401   ThrowableInformation getThrowableInformation() {
402     return throwableInfo;
403   }
404
405   /**
406      Return this event's throwable's string[] representaion.
407   */
408   public
409   String[] getThrowableStrRep() {
410
411     if(throwableInfo ==  null) {
412         return null;
413     } else {
414         return throwableInfo.getThrowableStrRep();
415     }
416   }
417
418
419   private
420   void readLevel(ObjectInputStream ois)
421                       throws java.io.IOException, ClassNotFoundException {
422
423     int p = ois.readInt();
424     try {
425       String className = (String) ois.readObject();
426       if(className == null) {
427         level = Level.toLevel(p);
428       } else {
429         Method m = (Method) methodCache.get(className);
430         if(m == null) {
431           Class clazz = Loader.loadClass(className);
432           // Note that we use Class.getDeclaredMethod instead of
433           // Class.getMethod. This assumes that the Level subclass
434           // implements the toLevel(int) method which is a
435           // requirement. Actually, it does not make sense for Level
436           // subclasses NOT to implement this method. Also note that
437           // only Level can be subclassed and not Priority.
438           m = clazz.getDeclaredMethod(TO_LEVEL, TO_LEVEL_PARAMS);
439           methodCache.put(className, m);
440         }
441         level = (Level) m.invoke(null,  new Integer[] { new Integer(p) } );
442       }
443     } catch(InvocationTargetException e) {
444         if (e.getTargetException() instanceof InterruptedException
445                 || e.getTargetException() instanceof InterruptedIOException) {
446             Thread.currentThread().interrupt();
447         }
448     LogLog.warn("Level deserialization failed, reverting to default.", e);
449         level = Level.toLevel(p);
450     } catch(NoSuchMethodException e) {
451         LogLog.warn("Level deserialization failed, reverting to default.", e);
452         level = Level.toLevel(p);
453     } catch(IllegalAccessException e) {
454         LogLog.warn("Level deserialization failed, reverting to default.", e);
455         level = Level.toLevel(p);
456     } catch(RuntimeException e) {
457         LogLog.warn("Level deserialization failed, reverting to default.", e);
458         level = Level.toLevel(p);
459     }
460   }
461
462   private void readObject(ObjectInputStream ois)
463                         throws java.io.IOException, ClassNotFoundException {
464     ois.defaultReadObject();
465     readLevel(ois);
466
467     // Make sure that no location info is available to Layouts
468     if(locationInfo == null) {
469         locationInfo = new LocationInfo(null, null);
470     }
471   }
472
473   private
474   void writeObject(ObjectOutputStream oos) throws java.io.IOException {
475     // Aside from returning the current thread name the wgetThreadName
476     // method sets the threadName variable.
477     this.getThreadName();
478
479     // This sets the renders the message in case it wasn't up to now.
480     this.getRenderedMessage();
481
482     // This call has a side effect of setting this.ndc and
483     // setting ndcLookupRequired to false if not already false.
484     this.getNDC();
485
486     // This call has a side effect of setting this.mdcCopy and
487     // setting mdcLookupRequired to false if not already false.
488     this.getMDCCopy();
489
490     // This sets the throwable sting representation of the event throwable.
491     this.getThrowableStrRep();
492
493     oos.defaultWriteObject();
494
495     // serialize this event's level
496     writeLevel(oos);
497   }
498
499   private
500   void writeLevel(ObjectOutputStream oos) throws java.io.IOException {
501
502     oos.writeInt(level.toInt());
503
504     Class clazz = level.getClass();
505     if(clazz == Level.class) {
506       oos.writeObject(null);
507     } else {
508       // writing directly the Class object would be nicer, except that
509       // serialized a Class object can not be read back by JDK
510       // 1.1.x. We have to resort to this hack instead.
511       oos.writeObject(clazz.getName());
512     }
513   }
514
515     /**
516      * Set value for MDC property.
517      * This adds the specified MDC property to the event.
518      * Access to the MDC is not synchronized, so this
519      * method should only be called when it is known that
520      * no other threads are accessing the MDC.
521      * @since 1.2.15
522      * @param propName
523      * @param propValue
524      */
525   public final void setProperty(final String propName,
526                           final String propValue) {
527         if (mdcCopy == null) {
528             getMDCCopy();
529         }
530         if (mdcCopy == null) {
531             mdcCopy = new Hashtable();
532         }
533         mdcCopy.put(propName, propValue);      
534   }
535
536     /**
537      * Return a property for this event. The return value can be null.
538      *
539      * Equivalent to getMDC(String) in log4j 1.2.  Provided
540      * for compatibility with log4j 1.3.
541      *
542      * @param key property name
543      * @return property value or null if property not set
544      * @since 1.2.15
545      */
546     public final String getProperty(final String key) {
547         Object value = getMDC(key);
548         String retval = null;
549         if (value != null) {
550             retval = value.toString();
551         }
552         return retval;
553     }
554
555     /**
556      * Check for the existence of location information without creating it
557      * (a byproduct of calling getLocationInformation).
558      * @return true if location information has been extracted.
559      * @since 1.2.15
560      */
561     public final boolean locationInformationExists() {
562       return (locationInfo != null);
563     }
564
565     /**
566      * Getter for the event's time stamp. The time stamp is calculated starting
567      * from 1970-01-01 GMT.
568      * @return timestamp
569      *
570      * @since 1.2.15
571      */
572     public final long getTimeStamp() {
573       return timeStamp;
574     }
575
576     /**
577      * Returns the set of the key values in the properties
578      * for the event.
579      *
580      * The returned set is unmodifiable by the caller.
581      *
582      * Provided for compatibility with log4j 1.3
583      *
584      * @return Set an unmodifiable set of the property keys.
585      * @since 1.2.15
586      */
587     public Set getPropertyKeySet() {
588       return getProperties().keySet();
589     }
590
591     /**
592      * Returns the set of properties
593      * for the event.
594      *
595      * The returned set is unmodifiable by the caller.
596      *
597      * Provided for compatibility with log4j 1.3
598      *
599      * @return Set an unmodifiable map of the properties.
600      * @since 1.2.15
601      */
602     public Map getProperties() {
603       getMDCCopy();
604       Map properties;
605       if (mdcCopy == null) {
606          properties = new HashMap();
607       } else {
608          properties = mdcCopy;
609       }
610       return Collections.unmodifiableMap(properties);
611     }
612
613     /**
614      * Get the fully qualified name of the calling logger sub-class/wrapper.
615      * Provided for compatibility with log4j 1.3
616      * @return fully qualified class name, may be null.
617      * @since 1.2.15
618      */
619     public String getFQNOfLoggerClass() {
620       return fqnOfCategoryClass;
621     }
622
623
624     /**
625      * This removes the specified MDC property from the event.
626      * Access to the MDC is not synchronized, so this
627      * method should only be called when it is known that
628      * no other threads are accessing the MDC.
629      * @param propName the property name to remove
630      * @since 1.2.16
631      */
632     public Object removeProperty(String propName) {
633         if (mdcCopy == null) {
634             getMDCCopy();
635         }
636         if (mdcCopy == null) {
637             mdcCopy = new Hashtable();
638         }
639         return mdcCopy.remove(propName);
640     }
641 }