001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 * 
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 * 
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.log4j.pattern;
019
020import org.apache.log4j.Category;
021import org.apache.log4j.Level;
022import org.apache.log4j.Logger;
023import org.apache.log4j.MDC;
024import org.apache.log4j.NDC;
025import org.apache.log4j.Priority;
026import org.apache.log4j.helpers.Loader;
027import org.apache.log4j.helpers.LogLog;
028import org.apache.log4j.spi.LocationInfo;
029import org.apache.log4j.spi.LoggerRepository;
030import org.apache.log4j.spi.RendererSupport;
031import org.apache.log4j.spi.ThrowableInformation;
032
033import java.io.ObjectInputStream;
034import java.io.ObjectOutputStream;
035import java.lang.reflect.Method;
036import java.util.Collections;
037import java.util.HashMap;
038import java.util.Hashtable;
039import java.util.Map;
040import java.util.Set;
041
042// Contributors:   Nelson Minar <nelson@monkey.org>
043//                 Wolf Siberski
044//                 Anders Kristensen <akristensen@dynamicsoft.com>
045
046/**
047 * This class is a copy of o.a.l.spi.LoggingEvent from
048 * log4j 1.2.15 which has been renamed to keep
049 * the same overall class name length to allow a
050 * serialization written with a prior instance of o.a.l.spi.LoggingEvent
051 * to be deserialized with this class just by mangling the class name
052 * in the byte stream.
053 *
054 */
055public class LogEvent implements java.io.Serializable {
056
057  private static long startTime = System.currentTimeMillis();
058
059  /** Fully qualified name of the calling category class. */
060  transient public final String fqnOfCategoryClass;
061
062  /** 
063   * The category of the logging event. This field is not serialized
064   * for performance reasons.
065   *
066   * <p>It is set by the LoggingEvent constructor or set by a remote
067   * entity after deserialization.
068   * 
069   * @deprecated This field will be marked as private or be completely
070   * removed in future releases. Please do not use it.
071   * */
072  transient private Category logger;
073
074  /** 
075   * <p>The category (logger) name.
076   *   
077   * @deprecated This field will be marked as private in future
078   * releases. Please do not access it directly. Use the {@link
079   * #getLoggerName} method instead.
080
081   * */
082  final public String categoryName;
083
084  /** 
085   * Level of logging event. Level cannot be serializable because it
086   * is a flyweight.  Due to its special seralization it cannot be
087   * declared final either.
088   *   
089   * <p> This field should not be accessed directly. You shoud use the
090   * {@link #getLevel} method instead.
091   *
092   * @deprecated This field will be marked as private in future
093   * releases. Please do not access it directly. Use the {@link
094   * #getLevel} method instead.
095   * */
096  transient public Priority level;
097
098  /** The nested diagnostic context (NDC) of logging event. */
099  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 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    // Note the mdcCopy is used if it exists. Otherwise we use the MDC
321    // that is associated with the thread.
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      // the clone call is required for asynchronous logging.
340      // See also bug #5932.
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          // Note that we use Class.getDeclaredMethod instead of
421          // Class.getMethod. This assumes that the Level subclass
422          // implements the toLevel(int) method which is a
423          // requirement. Actually, it does not make sense for Level
424          // subclasses NOT to implement this method. Also note that
425          // only Level can be subclassed and not Priority.
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    // Make sure that no location info is available to Layouts
444    if(locationInfo == null)
445      locationInfo = new LocationInfo(null, null);
446  }
447
448  private
449  void writeObject(ObjectOutputStream oos) throws java.io.IOException {
450    // Aside from returning the current thread name the wgetThreadName
451    // method sets the threadName variable.
452    this.getThreadName();
453
454    // This sets the renders the message in case it wasn't up to now.
455    this.getRenderedMessage();
456
457    // This call has a side effect of setting this.ndc and
458    // setting ndcLookupRequired to false if not already false.
459    this.getNDC();
460
461    // This call has a side effect of setting this.mdcCopy and
462    // setting mdcLookupRequired to false if not already false.
463    this.getMDCCopy();
464
465    // This sets the throwable sting representation of the event throwable.
466    this.getThrowableStrRep();
467
468    oos.defaultWriteObject();
469
470    // serialize this event's level
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      // writing directly the Class object would be nicer, except that
484      // serialized a Class object can not be read back by JDK
485      // 1.1.x. We have to resort to this hack instead.
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}