+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.pattern;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.MDC;
+import org.apache.log4j.NDC;
+import org.apache.log4j.Priority;
+import org.apache.log4j.helpers.Loader;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableInformation;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+// Contributors: Nelson Minar <nelson@monkey.org>
+// Wolf Siberski
+// Anders Kristensen <akristensen@dynamicsoft.com>
+
+/**
+ * This class is a copy of o.a.l.spi.LoggingEvent from
+ * log4j 1.2.15 which has been renamed to keep
+ * the same overall class name length to allow a
+ * serialization written with a prior instance of o.a.l.spi.LoggingEvent
+ * to be deserialized with this class just by mangling the class name
+ * in the byte stream.
+ *
+ */
+public class LogEvent implements java.io.Serializable {
+
+ private static long startTime = System.currentTimeMillis();
+
+ /** Fully qualified name of the calling category class. */
+ transient public final String fqnOfCategoryClass;
+
+ /**
+ * The category of the logging event. This field is not serialized
+ * for performance reasons.
+ *
+ * <p>It is set by the LoggingEvent constructor or set by a remote
+ * entity after deserialization.
+ *
+ * @deprecated This field will be marked as private or be completely
+ * removed in future releases. Please do not use it.
+ * */
+ transient private Category logger;
+
+ /**
+ * <p>The category (logger) name.
+ *
+ * @deprecated This field will be marked as private in future
+ * releases. Please do not access it directly. Use the {@link
+ * #getLoggerName} method instead.
+
+ * */
+ final public String categoryName;
+
+ /**
+ * Level of logging event. Level cannot be serializable because it
+ * is a flyweight. Due to its special seralization it cannot be
+ * declared final either.
+ *
+ * <p> This field should not be accessed directly. You shoud use the
+ * {@link #getLevel} method instead.
+ *
+ * @deprecated This field will be marked as private in future
+ * releases. Please do not access it directly. Use the {@link
+ * #getLevel} method instead.
+ * */
+ transient public Priority level;
+
+ /** The nested diagnostic context (NDC) of logging event. */
+ private String ndc;
+
+ /** The mapped diagnostic context (MDC) of logging event. */
+ private Hashtable mdcCopy;
+
+
+ /** Have we tried to do an NDC lookup? If we did, there is no need
+ * to do it again. Note that its value is always false when
+ * serialized. Thus, a receiving SocketNode will never use it's own
+ * (incorrect) NDC. See also writeObject method. */
+ private boolean ndcLookupRequired = true;
+
+
+ /** Have we tried to do an MDC lookup? If we did, there is no need
+ * to do it again. Note that its value is always false when
+ * serialized. See also the getMDC and getMDCCopy methods. */
+ private boolean mdcCopyLookupRequired = true;
+
+ /** The application supplied message of logging event. */
+ transient private Object message;
+
+ /** The application supplied message rendered through the log4j
+ objet rendering mechanism.*/
+ private String renderedMessage;
+
+ /** The name of thread in which this logging event was generated. */
+ private String threadName;
+
+
+ /** This
+ variable contains information about this event's throwable
+ */
+ private ThrowableInformation throwableInfo;
+
+ /** The number of milliseconds elapsed from 1/1/1970 until logging event
+ was created. */
+ public final long timeStamp;
+ /** Location information for the caller. */
+ private LocationInfo locationInfo;
+
+ // Serialization
+ static final long serialVersionUID = -868428216207166145L;
+
+ static final Integer[] PARAM_ARRAY = new Integer[1];
+ static final String TO_LEVEL = "toLevel";
+ static final Class[] TO_LEVEL_PARAMS = new Class[] {int.class};
+ static final Hashtable methodCache = new Hashtable(3); // use a tiny table
+
+ /**
+ Instantiate a LoggingEvent from the supplied parameters.
+
+ <p>Except {@link #timeStamp} all the other fields of
+ <code>LoggingEvent</code> are filled when actually needed.
+ <p>
+ @param logger The logger generating this event.
+ @param level The level of this event.
+ @param message The message of this event.
+ @param throwable The throwable of this event. */
+ public LogEvent(String fqnOfCategoryClass, Category logger,
+ Priority level, Object message, Throwable throwable) {
+ this.fqnOfCategoryClass = fqnOfCategoryClass;
+ this.logger = logger;
+ this.categoryName = logger.getName();
+ this.level = level;
+ this.message = message;
+ if(throwable != null) {
+ this.throwableInfo = new ThrowableInformation(throwable);
+ }
+ timeStamp = System.currentTimeMillis();
+ }
+
+ /**
+ Instantiate a LoggingEvent from the supplied parameters.
+
+ <p>Except {@link #timeStamp} all the other fields of
+ <code>LoggingEvent</code> are filled when actually needed.
+ <p>
+ @param logger The logger generating this event.
+ @param timeStamp the timestamp of this logging event
+ @param level The level of this event.
+ @param message The message of this event.
+ @param throwable The throwable of this event. */
+ public LogEvent(String fqnOfCategoryClass, Category logger,
+ long timeStamp, Priority level, Object message,
+ Throwable throwable) {
+ this.fqnOfCategoryClass = fqnOfCategoryClass;
+ this.logger = logger;
+ this.categoryName = logger.getName();
+ this.level = level;
+ this.message = message;
+ if(throwable != null) {
+ this.throwableInfo = new ThrowableInformation(throwable);
+ }
+
+ this.timeStamp = timeStamp;
+ }
+
+ /**
+ Create new instance.
+ @since 1.2.15
+ @param fqnOfCategoryClass Fully qualified class name
+ of Logger implementation.
+ @param logger The logger generating this event.
+ @param timeStamp the timestamp of this logging event
+ @param level The level of this event.
+ @param message The message of this event.
+ @param threadName thread name
+ @param throwable The throwable of this event.
+ @param ndc Nested diagnostic context
+ @param info Location info
+ @param properties MDC properties
+ */
+ public LogEvent(final String fqnOfCategoryClass,
+ final Logger logger,
+ final long timeStamp,
+ final Level level,
+ final Object message,
+ final String threadName,
+ final ThrowableInformation throwable,
+ final String ndc,
+ final LocationInfo info,
+ final java.util.Map properties) {
+ super();
+ this.fqnOfCategoryClass = fqnOfCategoryClass;
+ this.logger = logger;
+ if (logger != null) {
+ categoryName = logger.getName();
+ } else {
+ categoryName = null;
+ }
+ this.level = level;
+ this.message = message;
+ if(throwable != null) {
+ this.throwableInfo = throwable;
+ }
+
+ this.timeStamp = timeStamp;
+ this.threadName = threadName;
+ ndcLookupRequired = false;
+ this.ndc = ndc;
+ this.locationInfo = info;
+ mdcCopyLookupRequired = false;
+ if (properties != null) {
+ mdcCopy = new java.util.Hashtable(properties);
+ }
+ }
+
+ /**
+ Set the location information for this logging event. The collected
+ information is cached for future use.
+ */
+ public LocationInfo getLocationInformation() {
+ if(locationInfo == null) {
+ locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass);
+ }
+ return locationInfo;
+ }
+
+ /**
+ * Return the level of this event. Use this form instead of directly
+ * accessing the <code>level</code> field. */
+ public Level getLevel() {
+ return (Level) level;
+ }
+
+ /**
+ * Return the name of the logger. Use this form instead of directly
+ * accessing the <code>categoryName</code> field.
+ */
+ public String getLoggerName() {
+ return categoryName;
+ }
+
+ /**
+ Return the message for this logging event.
+
+ <p>Before serialization, the returned object is the message
+ passed by the user to generate the logging event. After
+ serialization, the returned value equals the String form of the
+ message possibly after object rendering.
+
+ @since 1.1 */
+ public
+ Object getMessage() {
+ if(message != null) {
+ return message;
+ } else {
+ return getRenderedMessage();
+ }
+ }
+
+ /**
+ * This method returns the NDC for this event. It will return the
+ * correct content even if the event was generated in a different
+ * thread or even on a different machine. The {@link NDC#get} method
+ * should <em>never</em> be called directly. */
+ public
+ String getNDC() {
+ if(ndcLookupRequired) {
+ ndcLookupRequired = false;
+ ndc = NDC.get();
+ }
+ return ndc;
+ }
+
+
+ /**
+ Returns the the context corresponding to the <code>key</code>
+ parameter. If there is a local MDC copy, possibly because we are
+ in a logging server or running inside AsyncAppender, then we
+ search for the key in MDC copy, if a value is found it is
+ returned. Otherwise, if the search in MDC copy returns a null
+ result, then the current thread's <code>MDC</code> is used.
+
+ <p>Note that <em>both</em> the local MDC copy and the current
+ thread's MDC are searched.
+
+ */
+ public
+ Object getMDC(String key) {
+ Object r;
+ // Note the mdcCopy is used if it exists. Otherwise we use the MDC
+ // that is associated with the thread.
+ if(mdcCopy != null) {
+ r = mdcCopy.get(key);
+ if(r != null) {
+ return r;
+ }
+ }
+ return MDC.get(key);
+ }
+
+ /**
+ Obtain a copy of this thread's MDC prior to serialization or
+ asynchronous logging.
+ */
+ public
+ void getMDCCopy() {
+ if(mdcCopyLookupRequired) {
+ mdcCopyLookupRequired = false;
+ // the clone call is required for asynchronous logging.
+ // See also bug #5932.
+ Hashtable t = MDC.getContext();
+ if(t != null) {
+ mdcCopy = (Hashtable) t.clone();
+ }
+ }
+ }
+
+ public
+ String getRenderedMessage() {
+ if(renderedMessage == null && message != null) {
+ if(message instanceof String) {
+ renderedMessage = (String) message;
+ } else {
+ LoggerRepository repository = logger.getLoggerRepository();
+
+ if(repository instanceof RendererSupport) {
+ RendererSupport rs = (RendererSupport) repository;
+ renderedMessage= rs.getRendererMap().findAndRender(message);
+ } else {
+ renderedMessage = message.toString();
+ }
+ }
+ }
+ return renderedMessage;
+ }
+
+ /**
+ Returns the time when the application started, in milliseconds
+ elapsed since 01.01.1970. */
+ public static long getStartTime() {
+ return startTime;
+ }
+
+ public
+ String getThreadName() {
+ if(threadName == null) {
+ threadName = (Thread.currentThread()).getName();
+ }
+ return threadName;
+ }
+
+ /**
+ Returns the throwable information contained within this
+ event. May be <code>null</code> if there is no such information.
+
+ <p>Note that the {@link Throwable} object contained within a
+ {@link ThrowableInformation} does not survive serialization.
+
+ @since 1.1 */
+ public
+ ThrowableInformation getThrowableInformation() {
+ return throwableInfo;
+ }
+
+ /**
+ Return this event's throwable's string[] representaion.
+ */
+ public
+ String[] getThrowableStrRep() {
+
+ if(throwableInfo == null) {
+ return null;
+ } else {
+ return throwableInfo.getThrowableStrRep();
+ }
+ }
+
+
+ private
+ void readLevel(ObjectInputStream ois)
+ throws java.io.IOException, ClassNotFoundException {
+
+ int p = ois.readInt();
+ try {
+ String className = (String) ois.readObject();
+ if(className == null) {
+ level = Level.toLevel(p);
+ } else {
+ Method m = (Method) methodCache.get(className);
+ if(m == null) {
+ Class clazz = Loader.loadClass(className);
+ // Note that we use Class.getDeclaredMethod instead of
+ // Class.getMethod. This assumes that the Level subclass
+ // implements the toLevel(int) method which is a
+ // requirement. Actually, it does not make sense for Level
+ // subclasses NOT to implement this method. Also note that
+ // only Level can be subclassed and not Priority.
+ m = clazz.getDeclaredMethod(TO_LEVEL, TO_LEVEL_PARAMS);
+ methodCache.put(className, m);
+ }
+ PARAM_ARRAY[0] = new Integer(p);
+ level = (Level) m.invoke(null, PARAM_ARRAY);
+ }
+ } catch(Exception e) {
+ LogLog.warn("Level deserialization failed, reverting to default.", e);
+ level = Level.toLevel(p);
+ }
+ }
+
+ private void readObject(ObjectInputStream ois)
+ throws java.io.IOException, ClassNotFoundException {
+ ois.defaultReadObject();
+ readLevel(ois);
+
+ // Make sure that no location info is available to Layouts
+ if(locationInfo == null) {
+ locationInfo = new LocationInfo(null, null);
+ }
+ }
+
+ private
+ void writeObject(ObjectOutputStream oos) throws java.io.IOException {
+ // Aside from returning the current thread name the wgetThreadName
+ // method sets the threadName variable.
+ this.getThreadName();
+
+ // This sets the renders the message in case it wasn't up to now.
+ this.getRenderedMessage();
+
+ // This call has a side effect of setting this.ndc and
+ // setting ndcLookupRequired to false if not already false.
+ this.getNDC();
+
+ // This call has a side effect of setting this.mdcCopy and
+ // setting mdcLookupRequired to false if not already false.
+ this.getMDCCopy();
+
+ // This sets the throwable sting representation of the event throwable.
+ this.getThrowableStrRep();
+
+ oos.defaultWriteObject();
+
+ // serialize this event's level
+ writeLevel(oos);
+ }
+
+ private
+ void writeLevel(ObjectOutputStream oos) throws java.io.IOException {
+
+ oos.writeInt(level.toInt());
+
+ Class clazz = level.getClass();
+ if(clazz == Level.class) {
+ oos.writeObject(null);
+ } else {
+ // writing directly the Class object would be nicer, except that
+ // serialized a Class object can not be read back by JDK
+ // 1.1.x. We have to resort to this hack instead.
+ oos.writeObject(clazz.getName());
+ }
+ }
+
+ /**
+ * Set value for MDC property.
+ * This adds the specified MDC property to the event.
+ * Access to the MDC is not synchronized, so this
+ * method should only be called when it is known that
+ * no other threads are accessing the MDC.
+ * @since 1.2.15
+ * @param propName
+ * @param propValue
+ */
+ public final void setProperty(final String propName,
+ final String propValue) {
+ if (mdcCopy == null) {
+ getMDCCopy();
+ }
+ if (mdcCopy == null) {
+ mdcCopy = new Hashtable();
+ }
+ mdcCopy.put(propName, propValue);
+ }
+
+ /**
+ * Return a property for this event. The return value can be null.
+ *
+ * Equivalent to getMDC(String) in log4j 1.2. Provided
+ * for compatibility with log4j 1.3.
+ *
+ * @param key property name
+ * @return property value or null if property not set
+ * @since 1.2.15
+ */
+ public final String getProperty(final String key) {
+ Object value = getMDC(key);
+ String retval = null;
+ if (value != null) {
+ retval = value.toString();
+ }
+ return retval;
+ }
+
+ /**
+ * Check for the existence of location information without creating it
+ * (a byproduct of calling getLocationInformation).
+ * @return true if location information has been extracted.
+ * @since 1.2.15
+ */
+ public final boolean locationInformationExists() {
+ return (locationInfo != null);
+ }
+
+ /**
+ * Getter for the event's time stamp. The time stamp is calculated starting
+ * from 1970-01-01 GMT.
+ * @return timestamp
+ *
+ * @since 1.2.15
+ */
+ public final long getTimeStamp() {
+ return timeStamp;
+ }
+
+ /**
+ * Returns the set of the key values in the properties
+ * for the event.
+ *
+ * The returned set is unmodifiable by the caller.
+ *
+ * Provided for compatibility with log4j 1.3
+ *
+ * @return Set an unmodifiable set of the property keys.
+ * @since 1.2.15
+ */
+ public Set getPropertyKeySet() {
+ return getProperties().keySet();
+ }
+
+ /**
+ * Returns the set of properties
+ * for the event.
+ *
+ * The returned set is unmodifiable by the caller.
+ *
+ * Provided for compatibility with log4j 1.3
+ *
+ * @return Set an unmodifiable map of the properties.
+ * @since 1.2.15
+ */
+ public Map getProperties() {
+ getMDCCopy();
+ Map properties;
+ if (mdcCopy == null) {
+ properties = new HashMap();
+ } else {
+ properties = mdcCopy;
+ }
+ return Collections.unmodifiableMap(properties);
+ }
+
+ /**
+ * Get the fully qualified name of the calling logger sub-class/wrapper.
+ * Provided for compatibility with log4j 1.3
+ * @return fully qualified class name, may be null.
+ * @since 1.2.15
+ */
+ public String getFQNOfLoggerClass() {
+ return fqnOfCategoryClass;
+ }
+
+
+
+}