View Javadoc

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