Save This Page
Home » slf4j-1.5.5 » org.apache » log4j » spi » [javadoc | source]
    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   }

Save This Page
Home » slf4j-1.5.5 » org.apache » log4j » spi » [javadoc | source]