1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * 
4    * Copyright (C) 1999-2006, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  
11  package ch.qos.logback.classic.spi;
12  
13  import java.io.Externalizable;
14  import java.io.IOException;
15  import java.io.ObjectInput;
16  import java.io.ObjectOutput;
17  
18  import org.slf4j.Logger;
19  import org.slf4j.LoggerFactory;
20  import org.slf4j.Marker;
21  import org.slf4j.helpers.MessageFormatter;
22  
23  import ch.qos.logback.classic.Level;
24  
25  /**
26   * The internal representation of logging events. When an affirmative decision
27   * is made to log then a <code>LoggingEvent</code> instance is created. This
28   * instance is passed around to the different Logback components.
29   * 
30   * <p>
31   * Writers of Logback components such as appenders should be aware of that some
32   * of the LoggingEvent fields are initialized lazily. Therefore, an appender
33   * wishing to output data to be later correctly read by a receiver, must
34   * initialize "lazy" fields prior to writing them out. See the
35   * {@link #prepareForDeferredProcessing()} method for the exact list.
36   * </p>
37   * 
38   * @author Ceki G&uuml;lc&uuml;
39   * @author S&eacute;bastien Pennec
40   */
41  public class LoggingEventExt implements Externalizable {
42  
43  	/**
44  	 * 
45  	 */
46  	private static final long serialVersionUID = 3022264832697160750L;
47  
48  	/**
49  	 * 
50  	 */
51  	private static long startTime = System.currentTimeMillis();
52  
53  	/**
54  	 * Fully qualified name of the calling Logger class. This field does not
55  	 * survive serialization.
56  	 * 
57  	 * <p>
58  	 * Note that the getCallerInformation() method relies on this fact.
59  	 */
60  	transient String fqnOfLoggerClass;
61  
62  	/**
63  	 * The name of thread in which this logging event was generated.
64  	 */
65  	private String threadName;
66  
67  	/**
68  	 * Level of logging event.
69  	 * 
70  	 * <p>
71  	 * This field should not be accessed directly. You shoud use the {@link
72  	 * #getLevel} method instead.
73  	 * </p>
74  	 * 
75  	 */
76  	private Level level;
77  
78  	private String message;
79  	private String formattedMessage;
80  
81  	private Object[] argumentArray;
82  
83  	private Logger logger;
84  
85  	private ThrowableProxy throwableInfo;
86  
87  	private CallerData[] callerDataArray;
88  
89  	private Marker marker;
90  
91  	/**
92  	 * The number of milliseconds elapsed from 1/1/1970 until logging event was
93  	 * created.
94  	 */
95  	private long timeStamp;
96  
97  	public LoggingEventExt() {
98  	}
99  
100 	public LoggingEventExt(String fqcn, Logger logger, Level level, String message,
101 			Throwable throwable, Object[] argArray) {
102 		this.fqnOfLoggerClass = fqcn;
103 		this.logger = logger;
104 		this.level = level;
105 		this.message = message;
106 
107 		if (throwable != null) {
108 			this.throwableInfo = new ThrowableProxy(throwable);
109 		}
110 		
111 		if (argArray != null) {
112 			formattedMessage = MessageFormatter.arrayFormat(message, argArray);
113 		} else {
114 			formattedMessage = message;
115 		}
116 		timeStamp = System.currentTimeMillis();
117 	}
118 
119 	public Object[] getArgumentArray() {
120 		return this.argumentArray;
121 	}
122 
123 	public Level getLevel() {
124 		return level;
125 	}
126 
127 	public String getThreadName() {
128 		if (threadName == null) {
129 			threadName = (Thread.currentThread()).getName();
130 		}
131 		return threadName;
132 	}
133 
134 	/**
135 	 * @param threadName
136 	 *          The threadName to set.
137 	 * @throws IllegalStateException
138 	 *           If threadName has been already set.
139 	 */
140 	public void setThreadName(String threadName) throws IllegalStateException {
141 		if (this.threadName != null) {
142 			throw new IllegalStateException("threadName has been already set");
143 		}
144 		this.threadName = threadName;
145 	}
146 
147 	/**
148 	 * Returns the throwable information contained within this event. May be
149 	 * <code>null</code> if there is no such information.
150 	 */
151 	public ThrowableProxy getThrowableInformation() {
152 		return throwableInfo;
153 	}
154 
155 	/**
156 	 * Set this event's throwable information.
157 	 */
158 	public void setThrowableInformation(ThrowableProxy ti) {
159 		if (throwableInfo != null) {
160 			throw new IllegalStateException(
161 					"ThrowableInformation has been already set.");
162 		} else {
163 			throwableInfo = ti;
164 		}
165 	}
166 
167 	/**
168 	 * This method should be called prior to serializing an event. It should also
169 	 * be called when using asynchronous logging.
170 	 */
171 	public void prepareForDeferredProcessing() {
172 		this.getThreadName();
173 	}
174 
175 	public Logger getLogger() {
176 		return logger;
177 	}
178 
179 	public void setLogger(Logger logger) {
180 		this.logger = logger;
181 	}
182 
183 	public String getMessage() {
184 		return message;
185 	}
186 
187 	public void setMessage(String message) {
188 		if (this.message != null) {
189 			throw new IllegalStateException(
190 					"The message for this event has been set already.");
191 		}
192 		this.message = message;
193 	}
194 
195 	public long getTimeStamp() {
196 		return timeStamp;
197 	}
198 
199 	public void setTimeStamp(long timeStamp) {
200 		this.timeStamp = timeStamp;
201 	}
202 
203 	public void setLevel(Level level) {
204 		if (this.level != null) {
205 			throw new IllegalStateException(
206 					"The level has been already set for this event.");
207 		}
208 		this.level = level;
209 	}
210 
211 	/**
212 	 * The time at which this class was loaded into memory, expressed in
213 	 * millisecond elapsed since the epoch (1.1.1970).
214 	 * 
215 	 * @return The time as measured when this class was loaded into memory.
216 	 */
217 	public static final long getStartTime() {
218 		return startTime;
219 	}
220 
221 	/**
222 	 * Get the caller information for this logging event. If caller information is
223 	 * null at the time of its invocation, this method extracts location
224 	 * information. The collected information is cached for future use.
225 	 * 
226 	 * <p>
227 	 * Note that after serialization it is impossible to correctly extract caller
228 	 * information.
229 	 * </p>
230 	 */
231 	public CallerData[] getCallerData() {
232 		// we rely on the fact that fqnOfLoggerClass does not survive
233 		// serialization
234 		if (callerDataArray == null && fqnOfLoggerClass != null) {
235 			callerDataArray = CallerData.extract(new Throwable(), fqnOfLoggerClass);
236 		}
237 		return callerDataArray;
238 	}
239 
240 	public void setCallerInformation(CallerData[] callerDataArray) {
241 		this.callerDataArray = callerDataArray;
242 	}
243 
244 	public Marker getMarker() {
245 		return marker;
246 	}
247 
248 	public void setMarker(Marker marker) {
249 		if (this.marker != null) {
250 			throw new IllegalStateException(
251 					"The marker has been already set for this event.");
252 		}
253 		this.marker = marker;
254 	}
255 
256 	public String getFormattedMessage() {
257 		return formattedMessage;
258 	}
259 
260 	public void readExternal(ObjectInput in) throws IOException,
261 			ClassNotFoundException {
262 		threadName = (String) in.readObject();
263 		message = (String) in.readObject();
264 		formattedMessage = (String)in.readObject();
265 		int levelInt = in.readInt();
266 		level = Level.toLevel(levelInt);
267 		String loggerName = (String) in.readObject();
268 		logger = LoggerFactory.getLogger(loggerName);
269 	}
270 
271 	public void writeExternal(ObjectOutput out) throws IOException {
272 		if (threadName != null) {
273 			out.writeObject(threadName);
274 		} else {
275 			out.writeObject("noThreadName");
276 		}
277 		out.writeObject(message);
278 		out.writeObject(formattedMessage);
279 		out.writeInt(level.levelInt);
280 		out.writeObject(logger.getName());
281 		// out.writeObject(throwableInfo);
282 		// out.writeObject(callerDataArray);
283 		// out.writeObject(marker);
284 	}
285 
286 }