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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org.apache.log4j.spi;
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;
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;
39 // Contributors: Nelson Minar <nelson@monkey.org>
41 // Anders Kristensen <akristensen@dynamicsoft.com>
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
49 <p>This class is of concern to those wishing to extend log4j.
51 @author Ceki Gülcü
52 @author James P. Cakalic
55 public class LoggingEvent implements java.io.Serializable {
57 private static long startTime = System.currentTimeMillis();
59 /** Fully qualified name of the calling category class. */
60 transient public final String fqnOfCategoryClass;
63 * The category of the logging event. This field is not serialized
64 * for performance reasons.
66 * <p>It is set by the LoggingEvent constructor or set by a remote
67 * entity after deserialization.
69 * @deprecated This field will be marked as private or be completely
70 * removed in future releases. Please do not use it.
72 transient private Category logger;
75 * <p>The category (logger) name.
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.
82 final public String categoryName;
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.
89 * <p> This field should not be accessed directly. You shoud use the
90 * {@link #getLevel} method instead.
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.
96 transient public Priority level;
98 /** The nested diagnostic context (NDC) of logging event. */
101 /** The mapped diagnostic context (MDC) of logging event. */
102 private Hashtable mdcCopy;
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;
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;
117 /** The application supplied message of logging event. */
118 transient private Object message;
120 /** The application supplied message rendered through the log4j
121 objet rendering mechanism.*/
122 private String renderedMessage;
124 /** The name of thread in which this logging event was generated. */
125 private String threadName;
129 variable contains information about this event's throwable
131 private ThrowableInformation throwableInfo;
133 /** The number of milliseconds elapsed from 1/1/1970 until logging event
135 public final long timeStamp;
136 /** Location information for the caller. */
137 private LocationInfo locationInfo;
140 static final long serialVersionUID = -868428216207166145L;
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
148 Instantiate a LoggingEvent from the supplied parameters.
150 <p>Except {@link #timeStamp} all the other fields of
151 <code>LoggingEvent</code> are filled when actually needed.
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();
163 this.message = message;
164 if(throwable != null) {
165 this.throwableInfo = new ThrowableInformation(throwable, logger);
167 timeStamp = System.currentTimeMillis();
171 Instantiate a LoggingEvent from the supplied parameters.
173 <p>Except {@link #timeStamp} all the other fields of
174 <code>LoggingEvent</code> are filled when actually needed.
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();
188 this.message = message;
189 if(throwable != null) {
190 this.throwableInfo = new ThrowableInformation(throwable, logger);
193 this.timeStamp = timeStamp;
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
211 public LoggingEvent(final String fqnOfCategoryClass,
212 final Category logger,
213 final long timeStamp,
215 final Object message,
216 final String threadName,
217 final ThrowableInformation throwable,
219 final LocationInfo info,
220 final java.util.Map properties) {
222 this.fqnOfCategoryClass = fqnOfCategoryClass;
223 this.logger = logger;
224 if (logger != null) {
225 categoryName = logger.getName();
230 this.message = message;
231 if(throwable != null) {
232 this.throwableInfo = throwable;
235 this.timeStamp = timeStamp;
236 this.threadName = threadName;
237 ndcLookupRequired = false;
239 this.locationInfo = info;
240 mdcCopyLookupRequired = false;
241 if (properties != null) {
242 mdcCopy = new java.util.Hashtable(properties);
248 Set the location information for this logging event. The collected
249 information is cached for future use.
251 public LocationInfo getLocationInformation() {
252 if(locationInfo == null) {
253 locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass);
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;
266 * Return the name of the logger. Use this form instead of directly
267 * accessing the <code>categoryName</code> field.
269 public String getLoggerName() {
274 * Gets the logger of the event.
275 * Use should be restricted to cloning events.
278 public Category getLogger() {
283 Return the message for this logging event.
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.
292 Object getMessage() {
293 if(message != null) {
296 return getRenderedMessage();
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. */
307 if(ndcLookupRequired) {
308 ndcLookupRequired = false;
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.
323 <p>Note that <em>both</em> the local MDC copy and the current
324 thread's MDC are searched.
328 Object getMDC(String key) {
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);
342 Obtain a copy of this thread's MDC prior to serialization or
343 asynchronous logging.
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();
353 mdcCopy = (Hashtable) t.clone();
359 String getRenderedMessage() {
360 if(renderedMessage == null && message != null) {
361 if(message instanceof String) {
362 renderedMessage = (String) message;
364 LoggerRepository repository = logger.getLoggerRepository();
366 if(repository instanceof RendererSupport) {
367 RendererSupport rs = (RendererSupport) repository;
368 renderedMessage= rs.getRendererMap().findAndRender(message);
370 renderedMessage = message.toString();
374 return renderedMessage;
378 Returns the time when the application started, in milliseconds
379 elapsed since 01.01.1970. */
380 public static long getStartTime() {
385 String getThreadName() {
386 if(threadName == null) {
387 threadName = (Thread.currentThread()).getName();
393 Returns the throwable information contained within this
394 event. May be <code>null</code> if there is no such information.
396 <p>Note that the {@link Throwable} object contained within a
397 {@link ThrowableInformation} does not survive serialization.
401 ThrowableInformation getThrowableInformation() {
402 return throwableInfo;
406 Return this event's throwable's string[] representaion.
409 String[] getThrowableStrRep() {
411 if(throwableInfo == null) {
414 return throwableInfo.getThrowableStrRep();
420 void readLevel(ObjectInputStream ois)
421 throws java.io.IOException, ClassNotFoundException {
423 int p = ois.readInt();
425 String className = (String) ois.readObject();
426 if(className == null) {
427 level = Level.toLevel(p);
429 Method m = (Method) methodCache.get(className);
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);
441 level = (Level) m.invoke(null, new Integer[] { new Integer(p) } );
443 } catch(InvocationTargetException e) {
444 if (e.getTargetException() instanceof InterruptedException
445 || e.getTargetException() instanceof InterruptedIOException) {
446 Thread.currentThread().interrupt();
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);
462 private void readObject(ObjectInputStream ois)
463 throws java.io.IOException, ClassNotFoundException {
464 ois.defaultReadObject();
467 // Make sure that no location info is available to Layouts
468 if(locationInfo == null) {
469 locationInfo = new LocationInfo(null, null);
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();
479 // This sets the renders the message in case it wasn't up to now.
480 this.getRenderedMessage();
482 // This call has a side effect of setting this.ndc and
483 // setting ndcLookupRequired to false if not already false.
486 // This call has a side effect of setting this.mdcCopy and
487 // setting mdcLookupRequired to false if not already false.
490 // This sets the throwable sting representation of the event throwable.
491 this.getThrowableStrRep();
493 oos.defaultWriteObject();
495 // serialize this event's level
500 void writeLevel(ObjectOutputStream oos) throws java.io.IOException {
502 oos.writeInt(level.toInt());
504 Class clazz = level.getClass();
505 if(clazz == Level.class) {
506 oos.writeObject(null);
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());
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.
525 public final void setProperty(final String propName,
526 final String propValue) {
527 if (mdcCopy == null) {
530 if (mdcCopy == null) {
531 mdcCopy = new Hashtable();
533 mdcCopy.put(propName, propValue);
537 * Return a property for this event. The return value can be null.
539 * Equivalent to getMDC(String) in log4j 1.2. Provided
540 * for compatibility with log4j 1.3.
542 * @param key property name
543 * @return property value or null if property not set
546 public final String getProperty(final String key) {
547 Object value = getMDC(key);
548 String retval = null;
550 retval = value.toString();
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.
561 public final boolean locationInformationExists() {
562 return (locationInfo != null);
566 * Getter for the event's time stamp. The time stamp is calculated starting
567 * from 1970-01-01 GMT.
572 public final long getTimeStamp() {
577 * Returns the set of the key values in the properties
580 * The returned set is unmodifiable by the caller.
582 * Provided for compatibility with log4j 1.3
584 * @return Set an unmodifiable set of the property keys.
587 public Set getPropertyKeySet() {
588 return getProperties().keySet();
592 * Returns the set of properties
595 * The returned set is unmodifiable by the caller.
597 * Provided for compatibility with log4j 1.3
599 * @return Set an unmodifiable map of the properties.
602 public Map getProperties() {
605 if (mdcCopy == null) {
606 properties = new HashMap();
608 properties = mdcCopy;
610 return Collections.unmodifiableMap(properties);
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.
619 public String getFQNOfLoggerClass() {
620 return fqnOfCategoryClass;
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
632 public Object removeProperty(String propName) {
633 if (mdcCopy == null) {
636 if (mdcCopy == null) {
637 mdcCopy = new Hashtable();
639 return mdcCopy.remove(propName);