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.pattern;
20 import org.apache.log4j.Category;
21 import org.apache.log4j.Level;
22 import org.apache.log4j.Logger;
23 import org.apache.log4j.MDC;
24 import org.apache.log4j.NDC;
25 import org.apache.log4j.Priority;
26 import org.apache.log4j.helpers.Loader;
27 import org.apache.log4j.helpers.LogLog;
28 import org.apache.log4j.spi.LocationInfo;
29 import org.apache.log4j.spi.LoggerRepository;
30 import org.apache.log4j.spi.RendererSupport;
31 import org.apache.log4j.spi.ThrowableInformation;
33 import java.io.ObjectInputStream;
34 import java.io.ObjectOutputStream;
35 import java.lang.reflect.Method;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.Hashtable;
42 // Contributors: Nelson Minar <nelson@monkey.org>
44 // Anders Kristensen <akristensen@dynamicsoft.com>
47 * This class is a copy of o.a.l.spi.LoggingEvent from
48 * log4j 1.2.15 which has been renamed to keep
49 * the same overall class name length to allow a
50 * serialization written with a prior instance of o.a.l.spi.LoggingEvent
51 * to be deserialized with this class just by mangling the class name
55 public class LogEvent 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 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();
163 this.message = message;
164 if(throwable != null) {
165 this.throwableInfo = new ThrowableInformation(throwable);
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 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();
188 this.message = message;
189 if(throwable != null) {
190 this.throwableInfo = new ThrowableInformation(throwable);
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 LogEvent(final String fqnOfCategoryClass,
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);
247 Set the location information for this logging event. The collected
248 information is cached for future use.
250 public LocationInfo getLocationInformation() {
251 if(locationInfo == null) {
252 locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass);
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;
265 * Return the name of the logger. Use this form instead of directly
266 * accessing the <code>categoryName</code> field.
268 public String getLoggerName() {
273 Return the message for this logging event.
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.
282 Object getMessage() {
283 if(message != null) {
286 return getRenderedMessage();
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. */
297 if(ndcLookupRequired) {
298 ndcLookupRequired = false;
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.
313 <p>Note that <em>both</em> the local MDC copy and the current
314 thread's MDC are searched.
318 Object getMDC(String key) {
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);
332 Obtain a copy of this thread's MDC prior to serialization or
333 asynchronous logging.
337 if(mdcCopyLookupRequired) {
338 mdcCopyLookupRequired = false;
339 // the clone call is required for asynchronous logging.
340 // See also bug #5932.
341 Hashtable t = MDC.getContext();
343 mdcCopy = (Hashtable) t.clone();
349 String getRenderedMessage() {
350 if(renderedMessage == null && message != null) {
351 if(message instanceof String) {
352 renderedMessage = (String) message;
354 LoggerRepository repository = logger.getLoggerRepository();
356 if(repository instanceof RendererSupport) {
357 RendererSupport rs = (RendererSupport) repository;
358 renderedMessage= rs.getRendererMap().findAndRender(message);
360 renderedMessage = message.toString();
364 return renderedMessage;
368 Returns the time when the application started, in milliseconds
369 elapsed since 01.01.1970. */
370 public static long getStartTime() {
375 String getThreadName() {
376 if(threadName == null) {
377 threadName = (Thread.currentThread()).getName();
383 Returns the throwable information contained within this
384 event. May be <code>null</code> if there is no such information.
386 <p>Note that the {@link Throwable} object contained within a
387 {@link ThrowableInformation} does not survive serialization.
391 ThrowableInformation getThrowableInformation() {
392 return throwableInfo;
396 Return this event's throwable's string[] representaion.
399 String[] getThrowableStrRep() {
401 if(throwableInfo == null) {
404 return throwableInfo.getThrowableStrRep();
410 void readLevel(ObjectInputStream ois)
411 throws java.io.IOException, ClassNotFoundException {
413 int p = ois.readInt();
415 String className = (String) ois.readObject();
416 if(className == null) {
417 level = Level.toLevel(p);
419 Method m = (Method) methodCache.get(className);
421 Class clazz = Loader.loadClass(className);
422 // Note that we use Class.getDeclaredMethod instead of
423 // Class.getMethod. This assumes that the Level subclass
424 // implements the toLevel(int) method which is a
425 // requirement. Actually, it does not make sense for Level
426 // subclasses NOT to implement this method. Also note that
427 // only Level can be subclassed and not Priority.
428 m = clazz.getDeclaredMethod(TO_LEVEL, TO_LEVEL_PARAMS);
429 methodCache.put(className, m);
431 PARAM_ARRAY[0] = new Integer(p);
432 level = (Level) m.invoke(null, PARAM_ARRAY);
434 } catch(Exception e) {
435 LogLog.warn("Level deserialization failed, reverting to default.", e);
436 level = Level.toLevel(p);
440 private void readObject(ObjectInputStream ois)
441 throws java.io.IOException, ClassNotFoundException {
442 ois.defaultReadObject();
445 // Make sure that no location info is available to Layouts
446 if(locationInfo == null) {
447 locationInfo = new LocationInfo(null, null);
452 void writeObject(ObjectOutputStream oos) throws java.io.IOException {
453 // Aside from returning the current thread name the wgetThreadName
454 // method sets the threadName variable.
455 this.getThreadName();
457 // This sets the renders the message in case it wasn't up to now.
458 this.getRenderedMessage();
460 // This call has a side effect of setting this.ndc and
461 // setting ndcLookupRequired to false if not already false.
464 // This call has a side effect of setting this.mdcCopy and
465 // setting mdcLookupRequired to false if not already false.
468 // This sets the throwable sting representation of the event throwable.
469 this.getThrowableStrRep();
471 oos.defaultWriteObject();
473 // serialize this event's level
478 void writeLevel(ObjectOutputStream oos) throws java.io.IOException {
480 oos.writeInt(level.toInt());
482 Class clazz = level.getClass();
483 if(clazz == Level.class) {
484 oos.writeObject(null);
486 // writing directly the Class object would be nicer, except that
487 // serialized a Class object can not be read back by JDK
488 // 1.1.x. We have to resort to this hack instead.
489 oos.writeObject(clazz.getName());
494 * Set value for MDC property.
495 * This adds the specified MDC property to the event.
496 * Access to the MDC is not synchronized, so this
497 * method should only be called when it is known that
498 * no other threads are accessing the MDC.
503 public final void setProperty(final String propName,
504 final String propValue) {
505 if (mdcCopy == null) {
508 if (mdcCopy == null) {
509 mdcCopy = new Hashtable();
511 mdcCopy.put(propName, propValue);
515 * Return a property for this event. The return value can be null.
517 * Equivalent to getMDC(String) in log4j 1.2. Provided
518 * for compatibility with log4j 1.3.
520 * @param key property name
521 * @return property value or null if property not set
524 public final String getProperty(final String key) {
525 Object value = getMDC(key);
526 String retval = null;
528 retval = value.toString();
534 * Check for the existence of location information without creating it
535 * (a byproduct of calling getLocationInformation).
536 * @return true if location information has been extracted.
539 public final boolean locationInformationExists() {
540 return (locationInfo != null);
544 * Getter for the event's time stamp. The time stamp is calculated starting
545 * from 1970-01-01 GMT.
550 public final long getTimeStamp() {
555 * Returns the set of the key values in the properties
558 * The returned set is unmodifiable by the caller.
560 * Provided for compatibility with log4j 1.3
562 * @return Set an unmodifiable set of the property keys.
565 public Set getPropertyKeySet() {
566 return getProperties().keySet();
570 * Returns the set of properties
573 * The returned set is unmodifiable by the caller.
575 * Provided for compatibility with log4j 1.3
577 * @return Set an unmodifiable map of the properties.
580 public Map getProperties() {
583 if (mdcCopy == null) {
584 properties = new HashMap();
586 properties = mdcCopy;
588 return Collections.unmodifiableMap(properties);
592 * Get the fully qualified name of the calling logger sub-class/wrapper.
593 * Provided for compatibility with log4j 1.3
594 * @return fully qualified class name, may be null.
597 public String getFQNOfLoggerClass() {
598 return fqnOfCategoryClass;